异步任务中的异常处理
在Swift的async/await编程中,错误处理是确保应用健壮性的关键环节。前一节介绍了try/await的基本用法和错误抛出机制,本节将进一步探讨异步任务中的异常处理策略。通过具体的场景和代码示例,我们将展示如何处理单个任务的错误、管理多个任务的异常,以及在实际开发中应用最佳实践,让你在面对复杂异步流程时游刃有余。
处理单个任务的异常
单个异步任务的异常处理是基础,通常通过do/catch捕获错误。例如,一个网络请求:
enum NetworkError: Error {
case invalidURL
case serverError(Int)
}
func fetchResource(from urlString: String) async throws -> String {
guard let url = URL(string: urlString) else { throw NetworkError.invalidURL }
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw NetworkError.serverError((response as? HTTPURLResponse)?.statusCode ?? 500)
}
return String(data: data, encoding: .utf8) ?? ""
}
Task {
do {
let result = try await fetchResource(from: "https://api.example.com/resource")
print("资源:\(result)")
} catch NetworkError.invalidURL {
print("无效的URL")
} catch NetworkError.serverError(let code) {
print("服务器错误,状态码:\(code)")
} catch {
print("未知错误:\(error)")
}
}
要点
- 细化错误类型:用
enum定义具体错误,便于针对性处理。 - 多层捕获:按错误类型分级捕获,未处理的留给通用
catch。
处理多个任务的异常
真实应用中,异步任务往往相互依赖,错误可能在任意环节发生。以下是一个多任务场景的异常处理:
func fetchUser(id: String) async throws -> User {
let url = URL(string: "https://api.example.com/user/\(id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode(User.self, from: data)
}
func fetchPosts(for user: User) async throws -> [Post] {
let url = URL(string: "https://api.example.com/posts?user=\(user.id)")!
let (data, _) = try await URLSession.shared.data(from: url)
return try JSONDecoder().decode([Post].self, from: data)
}
Task {
do {
let user = try await fetchUser(id: "123")
let posts = try await fetchPosts(for: user)
print("用户:\(user.name), 帖子数:\(posts.count)")
} catch let error as URLError {
print("网络错误:\(error.localizedDescription)")
} catch let error as DecodingError {
print("解析错误:\(error.localizedDescription)")
} catch {
print("其他错误:\(error)")
}
}
分析
- 顺序依赖:
fetchPosts依赖fetchUser的结果,错误可能在任一步骤抛出。 - 类型化捕获:分别处理
URLError(网络)和DecodingError(JSON解析),提高精准性。 - 单一catch块:所有错误在一个
do/catch中处理,避免分散。
异常处理的进阶策略
复杂场景需要更灵活的异常处理,以下是几种常见策略:
1. 部分成功处理
当多个独立任务中部分失败时,可允许成功的部分继续执行:
func fetchImage(url: URL) async throws -> UIImage {
let (data, _) = try await URLSession.shared.data(from: url)
guard let image = UIImage(data: data) else { throw NetworkError.invalidData }
return image
}
Task {
var images: [UIImage] = []
let urls = [
URL(string: "https://example.com/image1.jpg")!,
URL(string: "https://example.com/image2.jpg")!
]
for url in urls {
do {
let image = try await fetchImage(url: url)
images.append(image)
} catch {
print("加载\(url)失败:\(error)")
}
}
print("成功加载\(images.count)张图片")
}
- 每个任务独立尝试,失败不影响后续。
- 适合批量操作(如下载多张图片)。
2. 重试机制
对可恢复的错误(如网络超时)实施重试:
func fetchWithRetry(url: URL, attempts: Int = 3) async throws -> Data {
var lastError: Error?
for attempt in 1...attempts {
do {
let (data, _) = try await URLSession.shared.data(from: url)
return data
} catch {
lastError = error
if attempt < attempts {
try? await Task.sleep(nanoseconds: 1_000_000_000 * UInt64(attempt)) // 指数退避
print("第\(attempt)次重试...")
}
}
}
throw lastError ?? NetworkError.unknown
}
Task {
do {
let data = try await fetchWithRetry(url: URL(string: "https://api.example.com/data")!)
print("数据大小:\(data.count)")
} catch {
print("最终失败:\(error)")
}
}
- 失败后重试,逐步增加延迟。
- 适合网络不稳定的场景。
3. 回退策略
提供默认值或替代方案:
func fetchConfig() async throws -> String {
let url = URL(string: "https://api.example.com/config")!
let (data, _) = try await URLSession.shared.data(from: url)
return String(data: data, encoding: .utf8)!
}
func fetchConfigWithFallback() async -> String {
do {
return try await fetchConfig()
} catch {
print("获取配置失败,使用默认值:\(error)")
return "默认配置"
}
}
Task {
let config = await fetchConfigWithFallback()
print("配置:\(config)")
}
- 错误时返回默认值,确保程序继续运行。
最佳实践
定义层次化的错误类型
使用嵌套枚举区分错误来源和严重性:enum AppError: Error { case network(NetworkError) case decoding(DecodingError) }避免忽略错误
不要用try?掩盖问题,除非明确需要:// 不推荐 let result = try? await fetchData()日志与用户反馈
记录错误并适时通知用户:@MainActor func showError(_ error: Error) { print("日志:\(error)") alertLabel.text = "出错:\(error.localizedDescription)" }测试异常路径
模拟网络中断、数据损坏等情况,确保catch有效。
小结
异步任务中的异常处理需要根据任务特性灵活应对。try/await通过集中的错误管理,简化了单个和多个任务的异常处理流程。本节通过网络请求、重试和回退等场景,展示了其实用性和进阶策略。掌握这些技巧,你将能设计出健壮的异步代码。下一节将通过一个实战案例,整合这些知识,带你完成一个完整的异步应用。
内容说明
- 结构:从单个任务到多任务,再到进阶策略和实践,最后总结。
- 代码:包含单任务、多任务、重试等示例,突出异常处理的多样性。
- 语气:实践性且深入,适合技术书籍的核心章节。
- 衔接:承接前节(
try/await基础),预告后续(实战案例)。
