Swift 关于解决并发编程的新方式
本文主要介绍使用async和await简化并发编程,使得异步操作更加直观和易读。
基本概念
-
async标记一个函数是异步的,意味着它可能在执行过程中暂停以等待其他任务完成。 -
await用于调用异步函数,表示在这个调用处暂停,直到该异步操作完成并返回结果。
定义异步函数
异步函数通过在函数声明中使用 async 关键字来定义:
func fetchData() async -> String {
// 模拟异步操作,如网络请求
await Task.sleep(2 * 1_000_000_000) // 等待2秒
return "数据已获取"
}
调用异步函数
调用异步函数需要在 await 前加上 async,通常这些调用必须在异步上下文中进行。
func displayData() async {
let data = await fetchData()
print(data)
}
在异步上下文中调用
为了在常规代码中调用异步函数,你需要在异步上下文中进行,比如在 Task 内部或其他异步函数中:
Task {
await displayData()
}
错误处理
异步函数可以抛出错误(使用 throws),在调用时也需要处理这些错误:
func fetchData() async throws -> String {
// 模拟可能抛出错误的异步操作
if Bool.random() {
throw NSError(domain: "", code: 1, userInfo: nil)
}
return "数据已获取"
}
func displayData() async {
do {
let data = try await fetchData()
print(data)
} catch {
print("获取数据时出错: \(error)")
}
}
Task {
await displayData()
}
等待多个异步操作
使用 async 和 await,可以并行等待多个异步操作:
func fetchMultipleData() async {
async let data1 = fetchData()
async let data2 = fetchData()
let combinedData = await "\(data1), \(data2)"
print(combinedData)
}
Task {
await fetchMultipleData()
}
使用 Task
Task 可以启动异步任务。 Task 可在任意上下文中使用并且不需要 async 上下文。
Task {
let data = await fetchData()
print(data)
}
使用 MainActor
在需要更新 UI 的地方,可以使用 @MainActor 来确保代码在主线程上执行:
@MainActor
func updateUI(with data: String) {
// 更新 UI 的代码
}
func fetchDataAndUpdateUI() async {
let data = await fetchData()
await updateUI(with: data)
}
Task {
await fetchDataAndUpdateUI()
}
任务取消
可以检查任务是否被取消以进行响应:
func fetchData() async throws -> String {
for i in 1...10 {
try Task.checkCancellation()
print("Fetching \(i)")
await Task.sleep(500_000_000) // 等待0.5秒
}
return "数据已获取"
}
func fetchDataAndHandleCancellation() async {
do {
let data = try await fetchData()
print(data)
} catch is CancellationError {
print("任务已取消")
} catch {
print("获取数据时出错: \(error)")
}
}
let task = Task {
await fetchDataAndHandleCancellation()
}
// 模拟取消任务
task.cancel()
与传统回调模式的对比
传统回调模式:
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.global().async {
sleep(2)
completion("数据已获取")
}
}
fetchData { data in
print(data)
}
使用 async 和 await:
func fetchData() async -> String {
await Task.sleep(2 * 1_000_000_000)
return "数据已获取"
}
Task {
let data = await fetchData()
print(data)
}
总结
async 和 await 使得 Swift 中的异步代码更加易读和易于维护,减少了传统回调和嵌套闭包的复杂性。这种方法强调了异步操作的顺序执行,使代码更接近同步代码的风格,从而更容易理解和调试。
注意事项:
- 只有 Swift 5.5 及以上版本才支持
async和await。 - 这些特性在支持并发的上下文中才能使用,如
Task或者在标记为async的函数内。 - 只有 Swift 5.5 及以上版本才支持
@MainActor - Xcode 13: 提供了对 Swift 5.5 和其并发特性的支持。