一个受到async/await启发的库,让Swift中的异步编程变得轻松愉快!
Taskig使得在背景或主线程上执行代码变得简单,然后通过async/await方法组合这些任务。Taskig深受AsyncTask(《https://github.com/zhxnlai/AsyncTask》)的启发。
特性
- Taskig是可组合的,允许您构建复杂的流程。
- Taskig支持原生
do-catch
和try
形式的错误处理。 - Taskig是
面向协议的
,您可以将任何对象转换为Task。
没有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()
安装
CocoaPods
您可以通过CocoaPods来安装Taskig。要安装它,只需将以下行添加到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使用调度队列执行任务,因此所有标准调度队列都可用于执行:背景、工具、用户启动、用户交互和主,但您也可以使用针对应用程序特定的队列。
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()
您可以使用并发参数来控制并发并行任务的量
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
协议,只需指定一个动作和返回类型。
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文件。