将旧式回调/Combine 接入 async/await
使用 withCheckedContinuation / withCheckedThrowingContinuation 包装完成处理器,并将 Combine 发布者接入 AsyncSequence ,以支持简单的 for await 循环。
将旧式回调/Combine 接入 async/await 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 3 节课。
为什么要桥接?
目标:使用延续包装回调,并将 Combine 流作为 AsyncSequence 处理。
- 延续(不抛出错误/抛出错误)
- URLSession 桥接示例
- Combine →
for await消费
不抛出错误的延续
当旧式 API 不会失败时,请使用 withCheckedContinuation。仅调用一次 resume。
// Old-style API
func fetchName(completion: @escaping (String) -> Void) {
// simulate async
Task { try? await Task.sleep(nanoseconds: 80_000_000); completion("Ana") }
}
// Async wrapper using continuation (non-throwing)
func fetchName() async -> String {
await withCheckedContinuation { (cont: CheckedContinuation<String, Never>) in
fetchName { value in cont.resume(returning: value) }
}
}
Task {
let name = await fetchName()
print("name:", name) // "Ana"
}抛出错误的延续
使用 withCheckedThrowingContinuation,通过 throw 暴露旧式 API 的错误。确保只有一条调用 resume 的路径。
enum NetError: Error { case offline; case badStatus }
func loadNumber(completion: @escaping (Result<Int, Error>) -> Void) {
Task {
try? await Task.sleep(nanoseconds: 80_000_000)
completion(.failure(NetError.offline))
}
}
func loadNumber() async throws -> Int {
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Int, Error>) in
loadNumber { result in
switch result {
case .success(let v): cont.resume(returning: v)
case .failure(let e): cont.resume(throwing: e)
}
}
}
}
Task {
do {
let n = try await loadNumber()
print(n)
} catch {
print("error:", error) // offline
}
}URLSession 桥接
包装完成处理程序 API(例如 URLSession),以获得清晰的异步抛出错误函数。请记住仅调用一次 resume。
import Foundation
func getText(from url: URL) async throws -> String {
try await withCheckedThrowingContinuation { (cont: CheckedContinuation<String, Error>) in
let task = URLSession.shared.dataTask(with: url) { data, resp, err in
if let err = err { cont.resume(throwing: err); return }
guard let data = data, let text = String(data: data, encoding: .utf8) else {
cont.resume(throwing: URLError(.badServerResponse)); return
}
cont.resume(returning: text)
}
task.resume()
}
}
// Demo (works in environments with network access)
let demoURL = URL(string: "https://example.com")!
Task {
do { let body = try await getText(from: demoURL); print(body.prefix(15)) }
catch { print("network error:", error) }
}将 Combine 用作 AsyncSequence
许多 Combine 发布者会暴露 .values,让您可以对发出的值使用 for await,而无需自定义桥接。
import Combine
import Foundation
let subject = PassthroughSubject<Int, Never>()
// Consume Combine as AsyncSequence:
Task {
var produced: [Int] = []
for await value in subject.values { // Publisher.values -> AsyncSequence
produced.append(value)
if produced.count == 3 { break } // stop after 3 values
}
print("collected:", produced) // e.g., [1,2,3]
}
// Publish a few values
var cancellables = Set<AnyCancellable>()
Timer.publish(every: 0.05, on: .main, in: .common)
.autoconnect()
.prefix(3)
.scan(0) { acc, _ in acc + 1 }
.sink { subject.send($0) }
.store(in: &cancellables)发布者 → AsyncStream
如果 .values 不可用,可以通过 AsyncStream 创建一个小型桥接。完成后不要忘记使用 cancel 取消订阅。
import Combine
// Bridge ANY Publisher to AsyncSequence using AsyncStream (simple version)
extension Publisher {
func asAsyncStream() -> AsyncStream<Output> {
AsyncStream { continuation in
let cancel = self.sink(
receiveCompletion: { _ in continuation.finish() },
receiveValue: { value in continuation.yield(value) }
)
continuation.onTermination = { _ in cancel.cancel() }
}
}
}
// Demo:
let pub = [10, 20, 30].publisher
Task {
var sum = 0
for await v in pub.asAsyncStream() { sum += v }
print("sum:", sum) // 60
}使用抛出错误的延续
快速检查:将可能失败的回调桥接到异步代码的最佳方式是什么?
回顾
回顾:使用延续(抛出错误或不抛出错误)包装旧式回调,并使用 .values 或小型 AsyncStream 桥接来消费 Combine。确保 resume 只能调用一次,并在完成时取消订阅。
用 AI 导师学习 Swift — 免费
在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。
- 课程
- 122
- 课程
- 409
常见问题解答
「将旧式回调/Combine 接入 async/await」课时是免费的吗?
是的 — 「将旧式回调/Combine 接入 async/await」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 3 节课。
「将旧式回调/Combine 接入 async/await」这节课中我会学到什么?
使用 withCheckedContinuation / withCheckedThrowingContinuation 包装完成处理器,并将 Combine 发布者接入 AsyncSequence ,以支持简单的 for await 循环。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Swift Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 3 节。
「将旧式回调/Combine 接入 async/await」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Swift Academy 课中编写并运行代码吗?
能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 生产者/消费者、流水线与超时
- 将旧式回调/Combine 接入 async/await
- 测试异步代码(XCTest)