withCheckedThrowingContinuation 是 Swift Concurrency 中用于将基于回调的异步代码桥接到 async/await 模式的重要工具。让我详细介绍一下它的使用方法。
基本概念
withCheckedThrowingContinuation 用于将传统的回调式异步函数转换为 async/await 风格。它创建一个"续体"(continuation),让你能够在回调中恢复异步任务的执行。
函数签名
func withCheckedThrowingContinuation<T>(
_ operation: (CheckedContinuation<T, Error>) -> Void
) async throws -> T
基本使用模式
func asyncVersion() async throws -> ReturnType {
return try await withCheckedThrowingContinuation { continuation in
callbackBasedFunction { result in
// 在回调中处理结果并恢复续体
}
}
}
具体示例
1. 简单的网络请求
// 传统的回调式函数
func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
// 模拟网络请求
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
completion(.success("Data loaded"))
}
}
// 转换为 async/await 版本
func fetchData() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
fetchData { result in
continuation.resume(with: result)
}
}
}
// 使用
func useFetchData() async {
do {
let data = try await fetchData()
print("Received: \(data)")
} catch {
print("Error: \(error)")
}
}
2. 文件读取示例
// 回调版本
func readFile(at path: String, completion: @escaping (Result<Data, Error>) -> Void) {
DispatchQueue.global().async {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path))
completion(.success(data))
} catch {
completion(.failure(error))
}
}
}
// async/await 版本
func readFile(at path: String) async throws -> Data {
return try await withCheckedThrowingContinuation { continuation in
readFile(at: path) { result in
continuation.resume(with: result)
}
}
}
3. 你的工作目录示例分析
/// 获取当前工作目录(异步版本)
func getCurrentWorkingDirectory() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
// 调用原有的回调式函数
getCurrentWorkingDirectoryAsync { result in
// 在回调中将结果传递给续体
continuation.resume(with: result)
}
}
}
重要注意事项
1. 续体必须被调用且只能调用一次
// ❌ 错误:可能不调用续体
func badExample() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
someAsyncFunction { result in
if case .success = result {
continuation.resume(with: result) // 只在成功时调用
}
// 错误:失败时没有调用续体!
}
}
}
// ✅ 正确:确保总是调用一次
func goodExample() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
someAsyncFunction { result in
continuation.resume(with: result) // 总是调用
}
}
}
2. 处理取消
func cancellableOperation() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
let task = startSomeOperation { result in
continuation.resume(with: result)
}
// 设置取消处理
continuation.onCancel = {
task.cancel()
}
}
}
3. 内存安全
续体不是线程安全的,确保在正确的上下文中使用:
// ❌ 错误:可能在后台线程调用
func unsafeExample() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global().async {
// 这可能导致问题
continuation.resume(returning: "data")
}
}
}
// ✅ 正确:确保在适当的上下文中
func safeExample() async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
someAsyncFunction { result in
// 假设 someAsyncFunction 在正确的上下文中调用回调
continuation.resume(with: result)
}
}
}
相关函数
-
withCheckedContinuation: 用于不会抛出错误的异步操作 -
withUnsafeThrowingContinuation: 不安全的版本,性能更好但需要手动管理内存 -
withCheckedContinuation: 安全的非抛出版本
实际应用场景
- 桥接旧代码: 将现有的回调式库转换为 async/await
- 第三方库集成: 当第三方库不支持 async/await 时
- 复杂异步操作: 需要将多个回调组合成一个 async 函数时
你的工作目录函数就是典型的应用场景:将基于回调的异步操作转换为更现代的 async/await 模式,使代码更简洁、更易读。