1️⃣ 核心区别
| 特性 | AsyncStream | AsyncThrowingStream |
|---|---|---|
| 是否可以抛异常 | ❌ 不抛异常 | ✅ 可以抛异常 |
| next() 返回类型 | Element? | Result<Element, Error>? 或 try await Element? |
| 异步迭代方式 | for await element in stream | for try await element in stream |
| 用途 | 仅生成异步数据流 | 异步数据流中可能出现错误,需要传播给消费者 |
简单理解:AsyncStream = 安全不抛错的异步流,AsyncThrowingStream = 可能抛错的异步流。
2️⃣ AsyncStream 示例
let stream = AsyncStream<Int> { continuation in
Task {
for i in 0..<3 {
continuation.yield(i)
try? await Task.sleep(nanoseconds: 500_000_000)
}
continuation.finish()
}
}
for await n in stream {
print(n) // 输出 0 1 2
}
- 不会抛异常
- 结束时调用
continuation.finish()
3️⃣ AsyncThrowingStream 示例
enum MyError: Error { case badData }
let stream = AsyncThrowingStream<Int, MyError> { continuation in
Task {
for i in 0..<3 {
if i == 2 {
continuation.finish(throwing: .badData) // 抛异常结束
return
}
continuation.yield(i)
}
}
}
do {
for try await n in stream {
print(n) // 输出 0 1
}
} catch {
print(error) // 输出 badData
}
- 可以在流中抛出异常
- 消费者使用
for try await finish(throwing:)会触发 next() 抛错
4️⃣ 底层原理对比
-
AsyncStream
- 底层使用 Task + continuation
yield()→ resume 被挂起的 Taskfinish()→ next() 返回 nil
-
AsyncThrowingStream
- 同样使用 Task + continuation
yield()→ resume Taskfinish(throwing:)→ next() 抛出错误- 异常通过
throw传播给消费方
核心差异就是 是否允许 continuation finish 时携带 error。
5️⃣ 什么时候用哪个
| 场景 | 推荐类型 |
|---|---|
| 纯异步数据流,无错误情况 | AsyncStream |
| 异步数据流可能失败或需要抛异常 | AsyncThrowingStream |
| 需要错误传递给上层 Task 处理 | AsyncThrowingStream |
6️⃣ 面试必背总结
- AsyncStream:异步序列,不抛异常,使用
for await。- AsyncThrowingStream:异步序列,可能抛异常,使用
for try await,可以在finish(throwing:)或yield过程中传播错误。- 底层都是 Task + continuation,差异在于 finish 时是否允许携带异常。