iOS Swift Concurrency:用 async/await 优化网络请求代码
·
iOS Swift Concurrency:用 async/await 优化网络请求代码
Swift 5.5 引入的 async/await 彻底改变了异步编程范式,特别适合优化网络请求代码。以下是如何使用它替代传统回调模式:
1. 传统回调模式的问题
传统方式使用闭包处理异步结果,易导致"回调地狱":
func fetchUser(completion: @escaping (Result<User, Error>) -> Void) {
URLSession.shared.dataTask(with: userURL) { data, _, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(NetworkError.noData))
return
}
do {
let user = try JSONDecoder().decode(User.self, from: data)
completion(.success(user))
} catch {
completion(.failure(error))
}
}.resume()
}
// 调用时嵌套回调
fetchUser { result in
switch result {
case .success(let user):
fetchPosts(for: user.id) { /* 更多嵌套... */ }
case .failure(let error):
print(error)
}
}
2. async/await 重构方案
(1) 定义异步网络请求
// 声明 async 函数,自动管理线程切换
func fetchUser() async throws -> User {
let (data, _) = try await URLSession.shared.data(from: userURL)
return try JSONDecoder().decode(User.self, from: data)
}
func fetchPosts(userId: Int) async throws -> [Post] {
let url = postsURL.appendingPathComponent("\(userId)")
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Post].self, from: data)
}
(2) 顺序调用(无嵌套)
Task {
do {
let user = try await fetchUser()
let posts = try await fetchPosts(userId: user.id)
updateUI(user: user, posts: posts)
} catch {
showError(error)
}
}
(3) 并行请求优化
使用 async let 并行执行独立请求:
Task {
async let user = fetchUser() // 立即启动
async let posts = fetchPosts() // 立即启动
do {
// 同时等待两个结果
let (finalUser, finalPosts) = try await (user, posts)
updateUI(user: finalUser, posts: finalPosts)
} catch {
showError(error)
}
}
3. 关键优势
- 线性逻辑:代码顺序执行,消除回调嵌套
- 错误集中处理:单点
try/catch捕获所有异常 - 性能提升:
- 零线程爆炸风险(系统自动管理线程池)
- 挂起协程仅占用 2KB 内存(传统线程需 512KB)
- 可取消性:通过
Task句柄实现一键取消let task = Task { let data = try await heavyRequest() // ... } // 取消请求(自动传播取消信号) task.cancel()
4. 迁移建议
- 逐步替换
completionHandler为async函数 - 使用
@MainActor标注 UI 更新方法:@MainActor func updateUI(user: User, posts: [Post]) { tableView.reloadData() // 自动切换主线程 } - 兼容旧代码:用
withCheckedThrowingContinuation包装回调func legacyRequest() async throws -> Data { return try await withCheckedThrowingContinuation { continuation in oldFetchMethod { result in continuation.resume(with: result) } } }
完整示例
struct API {
static func loadFeed() async throws -> Feed {
let (data, _) = try await URLSession.shared.data(
from: URL(string: "https://api.example.com/feed")!
)
return try JSONDecoder().decode(Feed.self, from: data)
}
}
// 使用端
Task {
do {
let feed = try await API.loadFeed()
await MainActor.run {
self.feedView.update(with: feed)
}
} catch {
await MainActor.run {
self.showToast("加载失败: \(error.localizedDescription)")
}
}
}
注意:在 iOS 15+ 使用
URLSession的async方法,旧系统需通过@available做版本检查。
更多推荐
所有评论(0)