「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。
在 Swift5.5 中新引入了两个关键字:async 与 await。它们可以使我们能够用同步的方式来运行复杂的异步代码。
它们的使用方法和 Flutter 中的基本一致:
- 用 async 标记异步函数。
- 调用函数的时候,在前面添加 await。
在之前,如果我们编写异步函数,很有可能会造成地狱回调。这对代码的可读性和健壮性都造成了极大的损害。
例如,如果我们想编写代码,从服务器获取 100000 条天气记录,对它们进行处理以计算一段时间内的平均温度,然后将得到的平均温度上传回服务器,我们可能已经编写了以下代码:
func fetchWeatherHistory(completion: @escaping ([Double]) -> Void) {
// Complex networking code here; we'll just send back 100,000 random temperatures
DispatchQueue.global().async {
let results = (1...100_000).map { _ in Double.random(in: -10...30) }
completion(results)
}
}
func calculateAverageTemperature(for records: [Double], completion: @escaping (Double) -> Void) {
// Sum our array then divide by the array size
DispatchQueue.global().async {
let total = records.reduce(0, +)
let average = total / Double(records.count)
completion(average)
}
}
func upload(result: Double, completion: @escaping (String) -> Void) {
DispatchQueue.global().async {
//此处会调用网络接口
completion("OK")
}
}
上述函数都是异步函数,因此,我们并不是同步的执行函数的代码并返回一个值,而是传递给函数一个 closure,等 DispatchQueue 处理完 async 中的逻辑,才会回调 closure 并返回。
下面是上述函数的调用示例:
fetchWeatherHistory { records in
calculateAverageTemperature(for: records) { average in
upload(result: average) { response in
print("Server response: (response)")
}
}
}
通过代码,我们可以看出以下问题:
@escaping(String)-> Void这种语法格式很难阅读。- 容易产生地狱回调。
- completion handler 可能被多次调用,或者调用者会忘记调用。
从 Swift 5.5 开始,我们现在可以通过将函数标记为 async 来标识异步函数,而不是依赖于completion handler,如下所示:
func fetchWeatherHistory() async -> [Double] {
(1...100_000).map { _ in Double.random(in: -10...30) }
}
func calculateAverageTemperature(for records: [Double]) async -> Double {
let total = records.reduce(0, +)
let average = total / Double(records.count)
return average
}
func upload(result: Double) async -> String {
"OK"
}
下面是函数调用示例:
func processWeather() async {
let records = await fetchWeatherHistory()
let average = await calculateAverageTemperature(for: records)
let response = await upload(result: average)
print("Server response: (response)")
}
如你所见,所有的难阅读的闭包都消失了,代码可读性也变高了。变成了我们平常写的同步代码。
async 与 await 使用注意事项:
- 在同步函数中,我们是无法调用异步函数的,因为这是没有意义的,Swift 会直接报错。
- 使用 async 标记的函数可以调用另一个使用 async 标记的函数,也可以调用其他的同步函数。
- 如果 async 函数和同步函数可以以相同的方式调用,那 Swift 将按照与当前上下文匹配的方式去调用函数。即当前为异步环境,则会调用 async 函数,反之,则调用同步函数。