Yakka

Build Status Version Carthage compatible SwiftPM compatible Platform License

特性

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

Yakka 可以用于您只需要在后台异步运行的临时代码,也可以用于协调复杂系统中的可重用组件。有很多不同的方法可以解决这类问题 – 希望这个方法适合您!

基本原理

Yakka 涉及 3 个主要事物:

  1. 任务对象 (Task objects) - 这些封装了需要完成的工作。
  2. 线路对象 (Line objects) - 这些控制任务的启动。
  3. 进程对象 (Process objects) - 任务的在执行状态的伴侣,用于报告进度和完成情况。

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

如果您只想启动一个任务,也可以就地创建线路。 另外,它们也可以被保存并用于控制同时发生的事情的数量(这是它们的主要目的)。

如果您想将原本独立的任务组合成一个相关的组,以便您可以等待它们的组合完成,您可以使用 SerialTask 或 ParallelTask。 这些也只是 Task 的子类,因此您可以轻松创建它们,添加 onFinish 处理程序,并将它们发送到一条线路上。

GCD 在内部以下列方式使用:

在大多数情况下,您可以使用 Yakka 而无需关心 GCD。

例子

简单的工作

let work = Task { process in
    print("working...")
    process.succeed()
}
Line().addTask(work).onFinish { outcome in
    print("finished!")
}

请注意,同步和异步工作负载都受支持,只要您在完成时告诉进程对象即可。

稍微复杂的工作

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. 成功 | 已取消 | 失败

关于此的一些要点:

其他细节

关于内存管理的注意事项

任务在运行时保留自身,这样做是有意为之,目的是使它们更易于使用。 任务使用的工作队列也在运行时保留。 您要做的就是确保您的工作最终通过调用进程对象上的方法之一来完成,并且该进程对象不会在该点之后被强引用。

SerialTask 和 ParallelTask 都会保留您提供给它们的任务,无论它们是否已启动。 它们在运行时保留自身,因为它们也只是 Tasks。

线路不会保留自身,并且在临时情况下,它们将在超出范围时被释放,但是任务继续运行不需要它们。

onStart 和 onFinish 上的事件闭包可以保留任务,因为它们将在这些事件发生后被释放。 但是,onProgress 和 onRetry 闭包在任务的生命周期内被保留,因此您不希望在这些事件闭包中强捕获任务。

Yakka?

就像 Hard Yakka – 澳大利亚俚语中“努力工作”的意思。 它源自“yaga”,这是布里斯班地区原住民使用的 Yagara 语言中的一个术语。

要求 & 平台

安装

Cocoapods

Yakka 可通过 CocoaPods 获得。 要安装它,只需将以下行添加到您的 Podfile 中

pod "Yakka"

Carthage

可以使用 Carthage 安装 Yakka。 将以下内容添加到您的 Cartfile 中

github "KieranHarper/Yakka" ~> 2.0

Swift Package Manager

也支持通过 Swift Package Manager 安装。 将以下内容添加到您的 Package 文件中

dependencies: [
    .Package(url: "https://github.com/KieranHarper/Yakka.git", majorVersion: 2)
]

手动

只需从 Sources 目录中拖动文件即可!

作者

Kieran Harper, kieranjharper@gmail.com, @KieranTheTwit

许可证

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