Yakka
特性
Yakka 是一个用于协调执行任务的工具包。以下是它能做什么
- 使您在后台以任意方式执行异步工作并在完成后轻松了解它。
- 让您轻松地将独立的背景工作分组以及/或链式组形成可追踪的过程。
- 允许多个感兴趣方监听/跟踪后台工作的进度和结果。
- 如果需要,可以提供对涉及到的 GCD 执行队列的精细控制。
Yakka 可以用于您只需要在后台异步运行的可丢弃代码,或者可以用于在复杂系统中协调整件的可重用组件。解决这个问题有很多不同的方式 - 希望这个方法对您来说有用!
基本概念
Yakka 涉及三个主要元素
- 任务对象 - 这些封装需要执行的工作。
- 行对象 - 这些控制任务的启动。
- 进程对象 - 工作中的任务的伴侣,用于报告进度和完成。
您可以使用闭包就地创建任务,或者创建一个子类并在那里提供工作闭包。这取决于您是否希望工作可重用在其他地方。
如果您只想让任务开始,也可以直接创建行。或者它们可以被保留下来,用来控制同时发生的事件数量(这是它们的主要用途)。
如果您想要将其他独立任务分组到一个依赖于该组的任务中,以便您可以等待它们的完成,您可以使用SerialTask或ParallelTask。这些都是任务子类,因此您可以通过创建它们,添加一个onFinish处理程序,并将它们发送到一条线路来实现。
GCD在内部以以下方式使用:
- 任务工作执行发生在工作队列上(可以通过Process对象在需要时访问)。这是当行启动任务时分配的。
- 除非在初始化时提供一个特定的一个,否则线路将工作队列定义为一个全局并发后台队列。
- 除非您提供替代队列,否则反馈处理程序将在主队列上执行。提供反馈的对象可以提供一个默认队列来使用(即,在所有情况下覆盖主队列),也可以提供一个用于特定反馈处理程序的队列。
在大多数情况下,您可以使用Yakka,而不必关心GCD。
示例
简单任务
let work = Task { process in
print("working...")
process.succeed()
}
Line().addTask(work).onFinish { outcome in
print("finished!")
}
注意,只要您告知Process对象何时完成,就支持同步和异步工作负载。
非简单任务
let work = Task { process in
// do something here...
// a "process-aware task" would implement the following:
// if you can, report progress periodically like this:
process.progress(0.5) // percent 0..1
// or if you have to, provide progress via polling like this:
process.progress {
return someMethodWhichDeterminesPercentComplete()
}
// where it makes sense, check for cancellation and bail
if process.shouldCancel {
process.cancel()
return
}
// or if it's easier, respond to cancellation as needed
process.onShouldCancel {
process.cancel()
}
// finish up at some point with success or fail:
process.fail()
process.succeed()
}
work.onProgress { percent in
// update your UI etc
}
work.onStart {
// update your UI etc
}
work.onFinish { outcome in
// outcome is one of .successful, .failed, .cancelled)
}
Line().addTask(work)
简单并行分组
var tasks = [Task]()
for ii in 0...4 {
let t = Task { (process) in
print(ii)
process.succeed()
}
tasks.append(t)
}
Line().addTask(ParallelTask(involving: tasks)).onFinish { (outcome) in
print("all tasks have finished")
}
简单串行分组
var tasks = [Task]()
for ii in 0...4 {
let t = Task { (process) in
print(ii)
process.succeed()
}
tasks.append(t)
}
let group = SerialTask(involving: tasks)
Line().addTask(group).onFinish { (outcome) in
print("all tasks have finished")
}
可重复使用作业
class DigMassiveHole: Task {
let diameter: Float
let depth: Float
var numEmployees = 1
init(diameter: Float, depth: Float) {
// Some config
self.diameter = diameter
self.depth = depth
super.init()
// Define what this task does
workToDo { (process) in
print("doing some digging...")
process.succeed()
}
}
}
let dig = DigMassiveHole(diameter: 30, depth: 100)
dig.numEmployees = 5
Line().addTask(dig).onFinish { (outcome) in
print("finished digging!")
}
长时间运行的行
// Create a line which we'll keep around
let uploadLine = Line(maxConcurrentTasks: 5)
// Receive events of interest
uploadLine.onBecameEmpty {
print("upload line isn't busy")
}
uploadLine.onNextTaskStarted { task in
print("upload line started another task")
}
// Create some upload tasks
let first = Task { (process) in
print("first upload")
process.fail()
}
let second = Task { (process) in
print("second upload")
process.succeed()
}
let third = Task { (process) in
print("third upload")
process.succeed()
}
// Run a task now
uploadLine.addTask(first)
// Later... run some more!
uploadLine.addTasks([second, third])
uploadLine.add { () -> Task in
return someMethodWhichCreatesATask()
}
// Anytime later...
uploadLine.stop() // or
uploadLine.stopAndCancel()
使用算子链式调用
let someProcess = task1 --> task2 --> task3 // serial
let anotherProcess = taskA --> taskB --> taskC // serial
let overall = someProcess ||| anotherProcess // parallel
overall.onFinish { outcome in
print("all tasks finished")
}
Line().addTask(overall)
以下示例都是在工作关闭结束前完成其工作(它们是同步的),但您可以查看测试文件以获取更多示例,其中工作在任意较晚的时间完成。
Yakka任务的生命周期
- 未开始
- 运行
- 取消中
- 成功 | 取消 | 失败
有关此的几点说明
- 向下流动,从不回头。
- 如果任务知道如何取消并跳出,则取消只会导致取消。
- 如果任务从未进入运行状态,则永远不会调用处理程序。
- 任务只在运行和取消时保留自己。
- 因为它从未回头流动,所以即使取消,任务也无法重新启动。
其他详细信息
- 任务在开始后保留自身,直到调用完成闭包。
- 传递给任务的工作闭包的Process对象可以从任何线程安全地交互。
- 根据你的工作提供进度,通过推送或拉取(轮询)或根本不提供。
- 根据你的工作通过推送或拉取或根本不检测来检测和提供取消请求。
- 任务实例是单次使用 - 完成后不能再次运行。
关于内存管理的说明
任务在运行期间会保留自身,这是有意为之,以便于使用。任务在运行期间也会保留工作队列。你只需确保通过调用进程对象的某个方法,确保工作最终完成,并且进程对象在此之后不会被强引用。
SerialTask 和 ParallelTask 都会保留你给予它们的任务,无论这些任务是否已启动。它们在运行期间也会保留自身,因为它们本质上也是任务。
行不会保留自身,在丢弃情况下,它们会随着离开作用域而被释放,但它们对于任务的持续运行并非必需。
onStart 和 onFinish 事件闭包可以用来保留任务,因为它们会在那些事件发生后被释放。然而,onProgress 和 onRetry 事件闭包在任务的生命周期内会被保留,所以你不想在那些事件闭包中强引用任务。
Yakka?
正如 Hard Yakka – 澳大利亚的经典俗语,意指工作。它来源于 'yaga',这是布鲁姆贝恩地区原住民语言中的一个术语。
要求 & 平台
- Swift 4.0
- iOS
- macOS
- watchOS
- tvOS
- Linux
安装
Cocoapods
Yakka 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中
pod "Yakka"
Carthage
可以使用 Carthage 安装 Yakka。将以下内容添加到您的 Cartfile 中
github "KieranHarper/Yakka" ~> 2.0
Swift 包管理器
通过 Swift 包管理器 的安装也受到支持。将以下内容添加到您的 Package 文件中
dependencies: [
.Package(url: "https://github.com/KieranHarper/Yakka.git", majorVersion: 2)
]
手动安装
只需将文件从 Sources 目录拖拽过来即可!
作者
基兰·哈珀,[email protected],@KieranTheTwit
许可证
Yakka 以 MIT 许可证提供。有关更多信息,请参阅 LICENSE 文件。