Queuer

GitHub Release Swift Versions Swift Platforms

特性

Queuer 是一个队列管理器,构建于 OperationQueueDispatch (又名 GCD) 之上。 它允许您轻松创建任何异步和同步任务,所有这些都由队列管理,只需几行代码即可。

以下是所有功能的列表

要求

Swift Queuer iOS macOS macCatalyst tvOS watchOS visionOS Linux
3.1...3.2 1.0.0...1.1.0 8.0+ 10.10+ 9.0+ 2.0+
4.0 1.3.0 8.0+ 10.10+ 9.0+ 2.0+
4.1 1.3.1...1.3.2 8.0+ 10.10+ 9.0+ 2.0+
4.2 2.0.0...2.0.1 8.0+ 10.10+ 9.0+ 3.0+
5.0...5.10 2.1.0...2.2.0 8.0+ 10.10+ 9.0+ 3.0+
5.9...5.10 3.0.0 12.0+ 10.13+ 13.0+ 12.0+ 4.0+ 1.0+

安装

请参阅 Requirements 部分以检查 Swift、Queuer 和 OS 版本。

在您的 Package.swift Swift Package Manager 清单中,将以下依赖项添加到您的 dependencies 参数

.package(url: "https://github.com/FabrizioBrancati/Queuer.git", from: "3.0.0"),

将依赖项添加到您在清单中声明的任何目标

.target(
    name: "MyTarget", 
    dependencies: [
        .product(name: "Queuer", package: "Queuer"),
    ]
),

用法

共享 Queuer

Queuer 提供了一个共享实例,您可以使用它将操作添加到中心化队列

Queuer.shared.addOperation(operation)

自定义队列

您还可以创建一个自定义队列

let queue = Queuer(name: "MyCustomQueue")

您甚至可以通过定义 maxConcurrentOperationCountqualityOfService 属性来创建队列

let queue = Queuer(name: "MyCustomQueue", maxConcurrentOperationCount: Int.max, qualityOfService: .default)

创建一个操作块

您有三种方法可以添加 Operation 块。

  1. 直接在 queue(或 Queuer.shared)上

    queue.addOperation {
        /// Your task here
    }
  2. 使用块创建一个 ConcurrentOperation

    let concurrentOperation = ConcurrentOperation { _ in
        /// Your task here
    }
    queue.addOperation(concurrentOperation)

注意

我们稍后会看到 ConcurrentOperation 的工作原理。

链式操作

链式操作是相互添加依赖关系的 Operation

它们遵循给定的数组顺序,例如: [A, B, C] = A -> B -> C -> completionBlock

let concurrentOperationA = ConcurrentOperation { _ in
    /// Your task A here
}
let concurrentOperationB = ConcurrentOperation { _ in
    /// Your task B here
}
queue.addChainedOperations([concurrentOperationA, concurrentOperationB]) {
    /// Your completion task here
}

您还可以在队列创建后添加一个 completionHandler,使用

queue.addCompletionHandler {
    /// Your completion task here
}

组操作

组操作是处理一组带有完成处理程序的 OperationOperation

允许执行一组带有完成处理程序的 Operation,该完成处理程序将在所有操作完成后调用,例如: [A -> [[B & C & D] -> completionHandler] -> E] -> completionHandler。 它通常应与 Chained Opetation 一起使用。

let groupOperationA = GroupOperation(
    [
        ConcurrentOperation { _ in
            /// Your task A here
        },
        ConcurrentOperation { _ in
            /// Your task B here
        }
    ]
)

let concurrentOperationC = ConcurrentOperation { _ in
    /// Your task C here
}

queue.addChainedOperations([groupOperationA, concurrentOperationC]) {
    /// Your completion task here
}

在这种情况下,输出将如下所示: [[A & B -> completionHandler] -> C] -> completionHandler

队列状态

有几种方法可以处理队列状态。

  1. 取消队列中的所有 Operation

    queue.cancelAll()
  2. 暂停队列

    queue.pause()

警告

通过调用 pause(),您不能确定每个 Operation 都会被暂停。 如果 Operation 已经启动,则在它是覆盖 pause() 函数的自定义 Operation 之前,它不会暂停。

  1. 恢复队列

    queue.resume()

警告

要获得完整的 pauseresume 状态,您必须创建一个覆盖 pause()resume() 函数的自定义 Operation

  1. 等待直到所有 Operation 完成

    queue.waitUntilAllOperationsAreFinished()

重要

此函数意味着队列将阻塞当前线程,直到所有 Operation 完成。

同步队列

将队列的 maxConcurrentOperationCount 属性设置为 1 将确保一次只执行一个任务。

异步操作

ConcurrentOperation 是一个被创建用于被子类化的类。 它允许同步和异步任务,具有暂停和恢复状态,可以轻松添加到队列,并且可以使用块创建。

您可以通过子类化来创建您的自定义 ConcurrentOperation

您必须覆盖 execute() 函数,并在任务完成其工作以通知队列时,在其中调用 finish(success:) 函数。

为方便起见,它有一个带有完成块的 init 函数

let concurrentOperation = ConcurrentOperation { _ in
    /// Your task here
}
concurrentOperation.addToQueue(queue)

自动重试操作

一个 Operation 被传递给每个闭包,通过它您可以设置和处理重试功能。

默认情况下,重试功能被禁用,要启用它,只需将 success 属性设置为 falsesuccessfalse 时,Operation 将重试,直到达到 maximumRetries 属性值。 为了让 Operation 知道何时一切正常,您必须将 success 设置为 true

通过 currentAttempt,您可以知道 Operation 处于哪个尝试次数。

let concurrentOperation = ConcurrentOperation { operation in
    /// Your task here
    if /* Successful */ {
      operation.success = true
    } else {
      operation.success = false
    }
}

手动重试操作

当您认为执行会成功时,您可以手动重试 Operation

一个 Operation 被传递给每个闭包,通过它您可以设置和处理重试功能。

默认情况下,手动重试功能被禁用,要启用它,只需将 manualRetry 属性设置为 true,您必须在执行闭包之外执行此操作。 您还必须将 success 设置为 truefalse,以让 Operation 知道何时一切正常,就像自动重试功能一样。

要让 Operation 重试您的执行闭包,您必须调用 retry() 函数。 确保至少调用 maximumRetries 次,如果您调用 retry() 的次数多于需要的次数,则没有问题,您的执行闭包将不会执行超过 maximumRetries 值的次数。

let concurrentOperation = ConcurrentOperation { operation in
    /// Your task here
    if /* Successful */ {
      operation.success = true
    } else {
      operation.success = false
    }
}
concurrentOperation.manualRetry = true

/// Later on your code
concurrentOperation.retry()

注意

如果未调用 retry() 函数,您可能会阻塞整个队列。

手动完成操作

默认情况下,Operation 将在执行闭包完成时完成其工作。

这意味着,如果您的闭包中有一个异步任务,Operation 将在任务完成之前完成。

如果您想手动完成它,您可以将 manualFinish 属性设置为 true,并在任务完成时调用 finish(success:) 函数。

使用 success 参数将 finish(success:) 函数调用为 truefalse,以让 Operation 知道任务是否成功。 如果您没有显式设置 success 参数,它将被设置为 true

let concurrentOperation = ConcurrentOperation { operation in
    /// Your asynchonous task here
}
concurrentOperation.manualFinish = true

/// Later on your code in your asynchronous task
concurrentOperation.finish(success: true)

注意

如果您不调用 finish(success:) 函数,您的队列将被阻塞,并且永远不会结束。

操作中的异步任务

如果您想使用 async/await 任务,您需要将 manualFinish 设置为 true,以便完全控制 Operation 生命周期。

注意

阅读更多关于手动完成 Operation 的信息 这里

let concurrentOperation = ConcurrentOperation { operation in
    Task {
        /// Your asynchonous task here
        operation.finish(success: true) // or false
    }
    /// Your asynchonous task here
}
concurrentOperation.manualFinish = true

注意

如果您不将 manualFinish 设置为 true,您的 Operation 将在异步任务完成之前完成。

调度器

Scheduler 是一个结构体,它使用 GDC 的 DispatchSourceTimer 来创建一个定时器,该定时器可以使用指定的间隔和服务质量执行函数。

let schedule = Scheduler(deadline: .now(), repeating: .seconds(1)) {
    /// Your task here
}

您甚至可以在没有处理程序的情况下创建一个 Scheduler,稍后再设置它

var schedule = Scheduler(deadline: .now(), repeating: .seconds(1))
schedule.setHandler {
    /// Your task here
}

通过 timer 属性,您可以访问所有 DispatchSourceTimer 属性和函数,例如 cancel()

schedule.timer.cancel()

信号量

Semaphore 是一个结构体,它使用 GCD 的 DispatchSemaphore 在函数上创建一个信号量,并等待它完成其工作。

提示

建议在 Semaphore 创建和 wait() 调用之后立即使用 defer { semaphore.continue() }

let semaphore = Semaphore()
semaphore.wait()
defer { semaphore.continue() }
/// Your task here

您甚至可以设置自定义超时,默认为 .distantFuture

semaphore.wait(DispatchTime(uptimeNanoseconds: 1_000_000_000))

如果在异步任务中使用它会更有用

let concurrentOperation = ConcurrentOperation {
    /// Your task here
    semaphore.continue()
}
concurrentOperation.addToQueue(queue)
semaphore.wait()

更新日志

要查看 Queuer 最近版本中的更改,请参阅 CHANGELOG.md 文件。

沟通

贡献

请参阅 CONTRIBUTING.md 文件。

许可证

Queuer 在 MIT 许可证下可用。 有关更多信息,请参阅 LICENSE 文件。