2-22.【Concurrency】AsyncSequence 是如何支持异步迭代的?底层原理是什么?

0 阅读2分钟

1️⃣ 核心概念

AsyncSequence 是一个异步序列接口,允许你按顺序异步获取元素,每次获取可能需要挂起等待数据。

  • 类似于 Sequence,但元素是 异步生成的
  • 迭代器遵循 AsyncIteratorProtocol
protocol AsyncIteratorProtocol {
    associatedtype Element
    mutating func next() async throws -> Element?
}
  • next() 是异步函数:

    • 返回一个元素或 nil(序列结束)
    • 可以挂起等待异步数据

2️⃣ 使用示例

struct Counter: AsyncSequence {
    typealias Element = Int

    let max: Int

    struct AsyncIterator: AsyncIteratorProtocol {
        var current = 0
        let max: Int

        mutating func next() async -> Int? {
            guard current < max else { return nil }
            let value = current
            current += 1
            try? await Task.sleep(nanoseconds: 500_000_000) // 模拟异步等待
            return value
        }
    }

    func makeAsyncIterator() -> AsyncIterator {
        AsyncIterator(current: 0, max: max)
    }
}

for await n in Counter(max: 3) {
    print(n) // 输出 0 1 2,每次等待 0.5 秒
}
  • for await 会自动调用 next(),并挂起当前 Task,等待异步结果
  • 使用 await 是因为元素可能尚未准备好

3️⃣ 底层原理

① AsyncSequence → AsyncIteratorProtocol

  • AsyncSequence 仅提供 makeAsyncIterator()
  • 核心异步逻辑在 next() 中实现

② next() 返回异步结果

  • next()async 函数 → 可以挂起

  • 底层由 continuation 或 Swift Concurrency runtime 支持:

    • 当前 Task 被挂起
    • 等待异步事件(网络、定时器、生产者)完成
    • 生成新元素后 resume 任务

③ 内部使用 Continuation / Task

  • Swift runtime 会为挂起的 next() 创建 continuation(类似回调容器)

  • 当数据可用:

    • 调用 resume,恢复被挂起的 Task
    • Task 恢复执行 for await 的下一次循环
for await item in AsyncSequence {
    ↓
next() async called → Task suspended → runtime保存continuation
Data ready → continuation.resume() → Task恢复

④ 异步流模型

  • 可以想象 AsyncSequence 是一个 异步数据流

    • 生产者:生成数据(可以是后台线程、定时器、网络回调)
    • 消费者:for await 循环读取数据
    • Task + continuation → 协作式挂起恢复

4️⃣ AsyncStream / AsyncThrowingStream

Swift 提供了方便的 构建 AsyncSequence 的工具

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)
}
  • yield → 放入 stream 队列,挂起的 Task 会被 resume
  • finish() → 结束 sequence,next() 返回 nil
  • 内部依赖 continuation + Task 排队 机制

5️⃣ 总结底层原理

  1. AsyncSequence/AsyncIteratorProtocol 定义异步迭代接口
  2. next() async 挂起 Task,等待数据生成
  3. Swift runtime + continuation 保存 Task 状态
  4. 数据生成 / yield / finish → resume Task
  5. for await 自动调用 next() 并挂起/恢复,像同步 for 循环一样可读

6️⃣ 面试必背结论

Swift 的 AsyncSequence 通过 async Iterator 协议和 next() async 方法实现异步迭代。底层使用 Task + continuation 模型:当元素尚不可用时 Task 挂起,生产者生成数据后通过 continuation 恢复 Task,从而实现协作式异步流的安全迭代。