一个受 async/await 启发的库,使 Swift 中的异步编程成为一种乐趣!
Taskig 可以轻松地在不同的线程(例如,后台或主线程)上执行代码,然后通过 async/await 方法组合这些任务。Taskig 的灵感很大程度上来自于 AsyncTask (https://github.com/zhxnlai/AsyncTask)
do-catch
和 try
的原生错误处理。没有 Taskig
// submit a task to the global queue for background execution
DispatchQueue.global(qos: .userInteractive).async {
let enhancedImage = self.applyImageFilter(image) // expensive operation taking a few seconds
// update UI on the main queue
DispatchQueue.main.async {
self.imageView.image = enhancedImage
UIView.animateWithDuration(0.3, animations: {
self.imageView.alpha = 1
}) { completed in
// add code to happen next here
}
}
}
使用 Taskig
Task.async {
let enhancedImage = self.applyImageFilter(image)
Task.async(executionQueue: .main) { self.imageView.image = enhancedImage }
UIView.animateTask(withDuration: 0.3) { self.label.alpha = 1 }.await()
// add code to happen next here
}
它甚至允许您扩展现有类型
let (data, response) = try! NSURL(string: "www.google.com")!.await()
Taskig 可通过 CocoaPods 获得。要安装它,只需将以下行添加到您的 Podfile
pod "Taskig"
在 Taskig 中,Task
代表异步操作的最终结果,就像其他库中的 Future 和 Promise 一样。它可以包装同步和异步 API。要创建 Task
,请使用闭包初始化它。为了使其可重用,请编写返回任务的函数。
// synchronous API wrapped in task
func encrypt(message: String) -> Task<String> {
return Task {
encrypt(message)
}
}
// asynchronous API wrapped in task
func get(URL: NSURL) -> Task<(NSData?, NSURLResponse?, NSError?)> {
return Task {completionHandler in
NSURLSession().dataTaskWithURL(URL, completionHandler: completionHandler).resume()
}
}
要获取 Task
的结果,请使用 async
或 await
。async
就像 dispatch_async
,您可以提供完成处理程序。相反,await
会阻塞当前线程并等待任务完成。为了避免主线程上的死锁,Taskig 包含一个前提条件检查,如果您尝试在主线程上调用 await
,它将导致崩溃!
// async
encrypt(message).async { ciphertext in /* do somthing */ }
get(URL).async {(data, response, error) in /* do somthing */ }
// await
let ciphertext = encrypt(message).await()
let (data, response, error) = get(URL).await()
当您创建任务时,您可以指定应在哪个队列上执行该任务。在底层,Taskig 使用 dispatch 队列来执行任务,因此所有标准 dispatch 队列都可用于执行:background、utility、userInitiated、userInteractive 和 main,但您也可以使用应用程序特定的队列。
Task<Void>(executionQueue: .main) {
print("On Main Thread")
//Update UI here
}
Task<Int>(executionQueue: .background) { () -> Int in
// Calculate something in the background
return 42
}
您可以使用多个 await 表达式来确保每个语句在执行下一个语句之前完成
Task {
print(“downloading image”)
var image = downloadImage.await()
imageView.updateWithImage(image).await()
print(“processing image”)
image = processImage(image).await()
imageView.updateWithImage(image).await()
print(“finished”)
}.async()
Taskig 还支持任务的集合、字典和序列。在它们两者上,您都可以调用 awaitFirst
或 awaitAll
以并行执行它们
// Get first result returned
let uLs = ["https://web1.swift.org", "https://web2.swift.org"]
let first = replicatedURLs.map(get).awaitFirst()
// Get all results
let messages = ["1", "2", "3"]
let all = messages.map(encrypt).awaitAll()
您可以使用 concurrency 参数控制并发并行任务的数量
let numbersStrings = (0...900).map{ String($0) }
// Maximum of 5 parallel tasks
let all = numbersStrings.map(encrypt).awaitAll(concurrency: 5)
Swift 为 错误处理 提供了第一类支持。在 Taskig 中,ThrowableTask
接受一个抛出闭包并传播错误。
extension String: Error {}
func toStringExceptZero(number: Int) -> ThrowableTask<String> {
return ThrowableTask<String> {
guard number != 0 else {
throw "FoundZero"
}
return "\(number)"
}
}
do {
try toStringExceptZero(number: 0).await()
} catch {
// Prints "FoundZero" error
print(error)
}
或者,您可以使用 awaitResult() 来获取结果枚举,该枚举可以是 .success(value)
(带有任务结果值)或 .failure(error)
(带有任务错误)。
if case let .failure(error) = toStringExceptZero(number: 0).awaitResult() {
print(error)
}
Taskig 是面向协议的;它定义了 TaskType
和 ThrowableTaskType
,并使用协议扩展提供了 async
、awaitResult
和 await
的默认实现。换句话说,这些协议很容易实现,您可以在任何符合它们的对象上 await
。能够扩展任务非常强大,因为它允许任务封装状态和行为。
在以下示例中,通过扩展 NSURL
使其成为 TaskType
,我们将数据获取作为 NSURL 类的一部分。要符合 TaskType
协议,只需指定一个 action 和返回类型。
extension URL: ThrowableTaskType {
typealias ReturnType = (Data, HTTPURLResponse)
public var executionQueue: DispatchQueue { return DispatchQueue.global() }
public func action(completion: @escaping (TaskResult<(Data, HTTPURLResponse)>) -> Void) {
URLSession.shared.dataTask(with: self) { (data, response, error) in
guard error == nil else {
completion(.failure(error!))
return
}
completion(.success((data!, response as! HTTPURLResponse)))
}.resume()
}
}
此扩展允许我们编写以下代码
let (data, response) = try! NSURL(string: "www.google.com")!.await()
ThrowableTaskType
任务可以通过 CancellableTaskType
协议支持取消。已取消的任务将抛出 CancellableTaskError.taskWasCancelled
错误。ThrowableTask
实现已经支持这一点。
var task = ThrowableTask<String> { () -> String in
return "Foobar"
}
task.isCancelled = true
do {
try task.await()
} catch {
// CancellableTaskError.taskWasCancelled thrown
print(error)
}
Thomas Sempf
Taskig 在 MIT 许可证下可用。有关更多信息,请参阅 LICENSE 文件。