Swift 的异步/等待控制流。
异步/等待将这个:
// example credit to: http://promisekit.org/chaining
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let md5 = md5ForData(data)
dispatch_async(dispatch_get_main_queue()) {
self.label.text = md5
UIView.animateWithDuration(0.3, animations: {
self.label.alpha = 1
}) {
// this is the end point
// add code to happen next here
}
}
}
变成这个:
async {
let md5 = md5ForData(data)
await { async(.Main) { self.label.text = md5 } }
await { UIView.animateWithDurationAsync(0.3) {self.label.alpha = 1} }
// this is the end point
// add code to happen next here
}() {}
Async 通过 CocoaPods 提供。要安装它,只需将以下行添加到您的 Podfile 中
pod "SwiftAsync"
示例项目和 测试文件 将帮助您入门。
要运行示例项目,首先克隆仓库,然后从 Example 目录中运行 pod install
。
先决条件
以下是创建异步函数的方式
let createImage = async {() -> UIImage in
sleep(3)
return UIImage()
}
以下是使用回调调用异步函数的方式
createImage() {image in
// do something with the image
}
以下是创建具有参数的异步函数的方式
let fetchImage = {(URL: NSURL) in
async {() -> UIImage in
// fetch the image synchronously
let image = get(URL)
return image
}
}
fetchImage(URL)() {image in
// do something with the image
}
让我们定义更多的异步函数
let processImage = {(image: UIImage) in
async {() -> UIImage in
sleep(1)
return image
}
}
let updateImageView = {(image: UIImage) in
async(.Main) {
self.imageView.image = image
}
}
与调用回调链式调用异步函数不同,使用 await
print("creating image")
createImage {image in
print("processing image")
processImage(image)() {image in
print("updating imageView")
updateImageView(image)() {
print("updated imageView")
}
}
}
async {
print("creating image")
var image = await { createImage }
print("processing image")
image = await { processImage(image) }
print("updating imageView")
await { updateImageView(image) }
print("updated imageView")
}() {}
await
是一个阻塞/同步函数。因此,它决不应该在主线程中调用。它执行一个异步函数,该函数是类型为 (T -> Void) -> Void
的闭包,并以同步方式返回结果。
async {
// blocks the thread until callback is called
let message = await {(callback: (String -> Void)) in
sleep(1)
callback("Hello")
}
print(message) // "Hello"
}() {}
// equivalent to
async {
let message = await {
async {() -> String in sleep(1); return "Hello" }
}
print(message) // "Hello"
}() {}
// equivalent to
async {
sleep(1)
let message = "Hello"
print(message) // "Hello"
} {}
以下是使用 await
包装异步 API(例如网络请求、动画等)并将它们同步化的方式
let session = NSURLSession(configuration: .ephemeralSessionConfiguration())
let get = {(URL: NSURL) in
async { () -> (NSData?, NSURLResponse?, NSError?) in
await {callback in session.dataTaskWithURL(URL, completionHandler: callback).resume()}
}
}
// with unwrapping
let get2 = {(URL: NSURL) in
async { () -> (NSData, NSURLResponse)? in
let (data, response, error) = await {callback in session.dataTaskWithURL(URL, completionHandler: callback).resume()}
guard let d = data, r = response where error != nil else { return nil }
return (d, r)
}
}
async {
if let (data, response) = await {get2(NSURL())} {
// do something
}
}() {}
或者,您还可以使用 thunkify
,它将具有尾随闭包的函数转换为异步函数
extension UIView {
class func animateWithDurationAsync(duration: NSTimeInterval, animations: () -> Void) -> (Bool -> Void) -> Void {
return thunkify(.Main, function: UIView.animateWithDuration)(duration, animations)
}
}
async {
await { UIView.animateWithDurationAsync(0.3) {self.label.alpha = 1} }
}() {}
要按顺序运行异步函数,我们可以使用 for/while 循环,因为 await
是阻塞/同步的。
let URLs = [NSURL]()
async {
var results = [NSData]()
for URL in URLs {
results.append(await { get(URL) })
}
print("fetched \(results.count) items in series")
}() {}
要并行运行异步函数,将与数组或字典中的异步函数一起调用 await
。
let URLs = [NSURL]()
async {
let results = await(parallel: URLs.map(get))
print("fetched \(results.count) items in parallel")
}() {}
默认情况下,异步函数在具有 全局并发队列 的 服务质量 QOS_CLASS_USER_INITIATED
上进行调度。要在不同的队列上调度
let taskOnMainThread = async(.Main) {
// do something
}
let customQueue = dispatch_queue_create("CustomQueueLabel", DISPATCH_QUEUE_CONCURRENT)
let taskOnCustomQueue = async(.Custom(customQueue)) {
// do something
}
默认情况下,await
会无限期等待异步函数完成。要添加超时
async {
await(timeout: 0.4) { async { () -> Bool in NSThread.sleepForTimeInterval(0.3); return true } }
}() {value in}
async$
和 await$
与 async
和 await
使用相同的 API。此外,它们处理抛出的错误
enum Error: ErrorType {
case TestError
}
let willThrow = async$ {() throws in
NSThread.sleepForTimeInterval(0.05)
throw Error.TestError
}
async$ {
try await${ willThrow }
}({(_, error) in
expect(error).to(beTruthy())
})
根据 闭包的强引用循环
如果将闭包赋给类实例的一个属性,并且闭包体捕获了该实例,也可能发生强引用循环。这种捕获可能是因为闭包体访问了实例的属性,例如 self.someProperty,或者因为闭包调用了实例的方法,例如 self.someMethod()。在这些情况下,这些访问导致闭包“捕获”self,创建一个强引用循环。
在顶级闭包的开头添加捕获列表可能会有所帮助。请查看示例项目以获取更多示例。
async {[weak self] in
self?.doSomething()
}
Async 可在 MIT 许可证下使用。有关更多信息,请参阅 LICENSE 文件。