Yakka 2.1.3

Yakka 2.1.3

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布上次发布2018年10月
SPM支持 SPM

Kieran Harper 维护。



Yakka 2.1.3

Yakka

Build Status Version Carthage compatible SwiftPM compatible Platform License

特性

Yakka 是一个用于协调执行任务的工具包。以下是它能做什么

  • 使您在后台以任意方式执行异步工作并在完成后轻松了解它。
  • 让您轻松地将独立的背景工作分组以及/或链式组形成可追踪的过程。
  • 允许多个感兴趣方监听/跟踪后台工作的进度和结果。
  • 如果需要,可以提供对涉及到的 GCD 执行队列的精细控制。

Yakka 可以用于您只需要在后台异步运行的可丢弃代码,或者可以用于在复杂系统中协调整件的可重用组件。解决这个问题有很多不同的方式 - 希望这个方法对您来说有用!

基本概念

Yakka 涉及三个主要元素

  1. 任务对象 - 这些封装需要执行的工作。
  2. 行对象 - 这些控制任务的启动。
  3. 进程对象 - 工作中的任务的伴侣,用于报告进度和完成。

您可以使用闭包就地创建任务,或者创建一个子类并在那里提供工作闭包。这取决于您是否希望工作可重用在其他地方。

如果您只想让任务开始,也可以直接创建行。或者它们可以被保留下来,用来控制同时发生的事件数量(这是它们的主要用途)。

如果您想要将其他独立任务分组到一个依赖于该组的任务中,以便您可以等待它们的完成,您可以使用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任务的生命周期

  1. 未开始
  2. 运行
  3. 取消中
  4. 成功 | 取消 | 失败

有关此的几点说明

  • 向下流动,从不回头。
  • 如果任务知道如何取消并跳出,则取消只会导致取消。
  • 如果任务从未进入运行状态,则永远不会调用处理程序。
  • 任务只在运行和取消时保留自己。
  • 因为它从未回头流动,所以即使取消,任务也无法重新启动。

其他详细信息

  • 任务在开始后保留自身,直到调用完成闭包。
  • 传递给任务的工作闭包的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 文件。