Swift AsyncSequence — 程式碼例項詳解
前言
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萬現金大獎」
- 在 SwiftUI 中建立一個環形 Slider
- Swift 週報 第二十五期
- Swift 週報 第二十四期
- 在 iOS 16 中用 SwiftUI Charts 建立一個折線圖
- Swift 中的 async/await ——程式碼例項詳解
- Swift AsyncSequence — 程式碼例項詳解
- Swift 週報 第十期
- SwiftUI 之 HStack 和 VStack 的切換
- 第三方庫並不是必須的
- Swift 週報 第十二期
- LeetCode - #146 LRU 快取(Top 100)
- LeetCode - #145 二叉樹的後序遍歷
- 現今 Swift 包中的二進位制目標
- LeetCode - #125 驗證迴文串
- 解決 iOS 15 上 APP 莫名其妙地退出登入
- 用 SwiftLint 保持 Swift 風格一致
- TCA - SwiftUI 的救星?(一)
- Swift 中的熱過載
- 在 Swift 中編寫指令碼:Git Hooks
- LeetCode - #124 二叉樹中的最大路徑和(Top 100)