Swift AsyncSequence — 程式碼例項詳解

語言: CN / TW / HK

前言

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 的工作原理。

下面的例子沿用了原始提案中的例子,實現了一個計數器。這些值可以立即使用,所以對非同步序列沒有太大的需求。然而,它確實展示了一個非同步序列的基本結構:

```swift 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,我們使用內部型別解決了這個問題。我們可以決定重寫此示例以消除對內部型別的需求:

```swift 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 開始迭代:

```swift for await count in Counter(limit: 5) { print(count) } print("Counter finished")

// Prints: // 1 // 2 // 3 // 4 // 5 // Counter finished ```

我們必須使用 await 關鍵字,因為我們可能會非同步接收數值。一旦不再有預期的值,我們就退出for迴圈。非同步序列的實現者可以通過在 next() 方法中返回 nil 來表示達到極限。在我們的例子中,一旦計數器達到配置的極限,或者迭代取消,我們就會達到這個預期:

```swift mutating func next() async -> Int? { guard !Task.isCancelled else { return nil }

guard current <= limit else {
    return nil
}

let result = current
current += 1
return result

} ```

許多常規的序列操作符也可用於非同步序列。其結果是,我們可以以非同步的方式執行對映和過濾等操作。

例如,我們可以只對偶數進行過濾:

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

// Prints: // 2 // 4 // Counter finished ```

或者我們可以在迭代之前將計數對映為一個 String

```swift 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 等方法。

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

注意,上述方法是非同步的,意味著它有可能無休止地等待一個值的存在,直到底層的 AsyncSequence 完成。

結論

AsyncSequence 是我們在Swift中熟悉的常規 Sequence 的非同步替代品。就像你不會經常自己建立一個自定義 Sequence 一樣,你也不太可能建立自定義的非同步序列。

本文正在參加「金石計劃 . 瓜分6萬現金大獎」