2-23.【Concurrency】AsyncStream 和 AsyncThrowingStream 的区别是什么?

3 阅读1分钟

1️⃣ 核心区别

特性AsyncStreamAsyncThrowingStream
是否可以抛异常❌ 不抛异常✅ 可以抛异常
next() 返回类型Element?Result<Element, Error>?try await Element?
异步迭代方式for await element in streamfor 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 被挂起的 Task
    • finish() → next() 返回 nil
  • AsyncThrowingStream

    • 同样使用 Task + continuation
    • yield() → resume Task
    • finish(throwing:) → next() 抛出错误
    • 异常通过 throw 传播给消费方

核心差异就是 是否允许 continuation finish 时携带 error


5️⃣ 什么时候用哪个

场景推荐类型
纯异步数据流,无错误情况AsyncStream
异步数据流可能失败或需要抛异常AsyncThrowingStream
需要错误传递给上层 Task 处理AsyncThrowingStream

6️⃣ 面试必背总结

  1. AsyncStream:异步序列,不抛异常,使用 for await
  2. AsyncThrowingStream:异步序列,可能抛异常,使用 for try await,可以在 finish(throwing:)yield 过程中传播错误。
  3. 底层都是 Task + continuation,差异在于 finish 时是否允许携带异常。