前言

AsyncSequence 是并发性框架SE-298 提案的一部分。它的名字意味着它是一个供给异步、顺序和迭代访问其元素的类型。换句话说:它是咱们在 Swift 中了解的惯例序列的一个异步变体。

就像你不会常常创立你的自界说序列相同,我不期望你常常创立一个自界说的 AsyncSequence 完结。然而,因为与 AsyncThrowingStream和AsyncStream 等类型一同运用,你很或许不得不与异步序列一同作业。因而,我将辅导你运用 AsyncSequence 实例进行作业。

什么是 AsyncSequence?

AsyncSequence 是咱们在Swift中了解的 Sequence 的一个异步变体。因为它的异步性,咱们需要运用 await 关键字,因为咱们要处理的是异步界说的办法。如果你没有运用过 async/await,我鼓励你阅览我的文章:Swift 中的async/await ——代码实例详解

值能够随着时间的推移而变得可用,这意味着一个 AsyncSequence 在你第一次运用它时或许不包括也或许包括一些,或许全部的值。

重要的是要理解 AsyncSequence 只是一个协议。它界说了怎么访问值,但并不发生或包括值。AsyncSequence 协议的完结者供给了一个 AsyncIterator,并担任开发和潜在地存储值。

创立 AsyncSequence

创立一个自界说的 AsyncSequence。

为了更好地理解 AsyncSequence 是怎么作业的,我将演示一个完结实例。然而,在界说你的 AsyncSequence 的自界说完结时,你或许想用 AsyncStream 来代替,因为它的设置更方便。因而,这只是一个代码比如,以更好地理解 AsyncSequence 的作业原理。

下面的比如沿用了原始提案中的比如,完结了一个计数器。这些值能够立即运用,所以对异步序列没有太大的需求。然而,它的确展示了一个异步序列的基本结构:

struct Counter: AsyncSequence {
    typealias Element = Int
    let limit: Int
    struct AsyncIterator : AsyncIteratorProtocol {
        let limit: Int
        var current = 1
        mutating func next() async -> Int? {
            guard !Task.isCancelled else {
                return nil
            }
            guard current <= limit else {
                return nil
            }
            let result = current
            current += 1
            return result
        }
    }
    func makeAsyncIterator() -> AsyncIterator {
        return AsyncIterator(howHigh: limit)
    }
}

如您所见,咱们界说了一个完结 AsyncSequence 协议的 Counter 结构体。该协议要求咱们返回一个自界说的 AsyncIterator,咱们运用内部类型处理了这个问题。咱们能够决定重写此示例以消除对内部类型的需求:

struct Counter: AsyncSequence, AsyncIteratorProtocol {
    typealias Element = Int
    let limit: Int
    var current = 1
    mutating func next() async -> Int? {
        guard !Task.isCancelled else {
            return nil
        }
        guard current <= limit else {
            return nil
        }
        let result = current
        current += 1
        return result
    }
    func makeAsyncIterator() -> Counter {
        self
    }
}

咱们现在能够将 self 作为迭代器返回,并保持所有逻辑的会集。

注意,咱们有必要通过供给 typealias 来协助编译器遵守 AsyncSequence 协议。

next() 办法担任对全体数值进行迭代。咱们的比如归结为供给尽或许多的计数值,直到咱们到达极限。咱们通过对 Task.isCancelled 的查看来完结撤销支撑。

异步序列的迭代

现在咱们知道了什么是 AsyncSequence 以及它是怎么完结的,现在是时分开始迭代这些值了。

以上述比如为例,咱们能够运用 Counter 开始迭代:

for await count in Counter(limit: 5) {
    print(count)
}
print("Counter finished")
// Prints:
// 1
// 2
// 3
// 4
// 5
// Counter finished

咱们有必要运用 await 关键字,因为咱们或许会异步接纳数值。一旦不再有预期的值,咱们就退出for循环。异步序列的完结者能够通过在 next() 办法中返回 nil 来表明到达极限。在咱们的比如中,一旦计数器到达装备的极限,或许迭代撤销,咱们就会到达这个预期:

mutating func next() async -> Int? {
    guard !Task.isCancelled else {
        return nil
    }
    guard current <= limit else {
        return nil
    }
    let result = current
    current += 1
    return result
}

许多惯例的序列操作符也可用于异步序列。其结果是,咱们能够以异步的方式执行映射和过滤等操作。

例如,咱们能够只对偶数进行过滤:

for await count in Counter(limit: 5).filter({ $0 % 2 == 0 }) {
    print(count)
}
print("Counter finished")
// Prints: 
// 2
// 4
// Counter finished

或许咱们能够在迭代之前将计数映射为一个 String

let counterStream = Counter(limit: 5)
    .map { $0 % 2 == 0 ? "Even" : "Odd" }
for await count in counterStream {
    print(count)
}
print("Counter finished")
// Prints:
// Odd
// Even
// Odd
// Even
// Odd
// Counter finished

咱们甚至能够运用 AsyncSequence 而不运用for循环,通过运用 contains 等办法。

let contains = await Counter(limit: 5).contains(3)
print(contains) // Prints: true

注意,上述办法是异步的,意味着它有或许无休止地等候一个值的存在,直到底层的 AsyncSequence 完结。

结论

AsyncSequence 是咱们在Swift中了解的惯例 Sequence 的异步替代品。就像你不会常常自己创立一个自界说 Sequence 相同,你也不太或许创立自界说的异步序列。

本文正在参加「金石计划 . 分割6万现金大奖」