Swift的异步编程库
AsyncTask远不止Future和Promise。
do-catch
和try
错误处理。在没有AsyncTask的情况下
// get a global concurrent queue
let queue = dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
// submit a task to the queue for background execution
dispatch_async(queue) {
let enhancedImage = self.applyImageFilter(image) // expensive operation taking a few seconds
// update UI on the main queue
dispatch_async(dispatch_get_main_queue()) {
self.imageView.image = enhancedImage
UIView.animateWithDuration(0.3, animations: {
self.imageView.alpha = 1
}) { completed in
// add code to happen next here
}
}
}
在有AsyncTask的情况下
Task {
let enhancedImage = self.applyImageFilter(image)
Task {self.imageView.image = enhancedImage}.async(.Main)
let completed = UIView.animateWithDurationAsync(0.3) { self.label.alpha = 1 }.await(.Main)
// add code to happen next here
}.async()
它甚至允许你扩展现有类型
let (data, response) = try! NSURL(string: "www.google.com")!.await()
In AsyncTask,一个Task
代表异步操作最终的成果,就像在其他库中的Future和Promise。它可以包装同步和异步API。要创建一个Task
,使用闭包进行初始化。要使其可重用,编写返回任务的函数。
// synchronous API
func encrypt(message: String) -> Task<String> {
return Task {
encrypt(message)
}
}
// asynchronous API
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
会阻塞当前线程并等待任务完成。
// 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()
你可以使用多个await表达式来确保每个语句在执行下一个语句之前完成
Task {
print(“downloading image”)
var image = UIImage(data: downloadImage.await())!
updateImageView(image).await(.Main)
print(“processing image”)
image = processImage(image).await()
updateImageView(image).await(.Main)
print(“finished”)
}.async()
你还可以在任务集合上调用awaitFirst
和awaitAll
以并行执行它们
let replicatedURLs = ["https://web1.swift.org", "https://web2.swift.org"]
let first = replicatedURLs.map(get).awaitFirst()
let messages = ["1", "2", "3"]
let all = messages.map(encrypt).awaitAll()
Swift为错误处理提供了first-class的支持。在AsyncTask中,一个ThrowableTask
接受一个抛出闭包并传播错误。
func load(path: String) -> ThrowableTask<NSData> {
return ThrowableTask {
switch path {
case "profile.png":
return NSData()
case "index.html":
return NSData()
default:
throw Error.NotFound
}
}
}
expect{try load("profile.png").await()}.notTo(throwError())
expect{try load("index.html").await()}.notTo(throwError())
expect{try load("random.txt").await()}.to(throwError())
AsyncTask 是一种基于协议的类;它定义了 TaskType
和 ThrowableTaskType
,并使用协议扩展提供了 async
和 await
的默认实现。换句话说,这些协议易于实现,并且您可以在任何符合它们的对象上使用 await
。任务能够扩展功能,因为它允许任务封装状态和行为。
在以下示例中,通过将 NSURL
扩展到 TaskType
,我们将数据获取作为 NSURL 类的一部分。要符合 TaskType
协议,只需指定一个动作和返回类型。
extension NSURL : ThrowableTaskType {
public typealias ReturnType = (NSData, NSURLResponse)
public func action(completion: Result<ReturnType> -> ()) {
ThrowableTask<ReturnType> {
let session = NSURLSession(configuration: .ephemeralSessionConfiguration())
let (data, response, error) = Task { callback in session.dataTaskWithURL(self, completionHandler: callback).resume()}.await()
guard error == nil else { throw error! }
return (data!, response!)
}.asyncResult(completion: completion)
}
}
本扩展允许我们编写如下代码
let (data, response) = try! NSURL(string: "www.google.com")!.await()
任务可以表示更复杂的活动,甚至包括涉及 UI 的活动。以下示例中,我们使用 ImagePickerTask
来启动 UIImagePickerViewController
并等待用户选择图片。一旦用户选择了图片或点击了取消按钮,视图控制器将消失,任务将返回所选择的图片。
class ImagePickerDemoViewController: UIViewController {
let imageView = UIImageView()
func launchImagePicker() {
Task {
do {
let data = try ImagePickerTask(viewController: self).await()
} catch Error.PhotoLibraryNotAvailable {
alert("Photo Library is Not Available")
}
guard let image = data?[UIImagePickerControllerOriginalImage] as? UIImage else {
self.imageView.image = nil
return
}
self.imageView.image = image
}.async()
}
}
ImagePickerTask
能够知道用户是否选择了图片或取消了选择,因为它是 UIImagePickerViewController
的代理。有关更多详情,请参阅其 实现 和示例文件夹。
要运行示例项目,请先克隆仓库,然后从 Example 目录运行 pod install
。
您也可以查看 测试用例。
AsyncTask 在 MIT 许可下提供。有关更多信息,请参阅 LICENSE 文件。