AsyncSequence
是并發(fā)性框架和SE-298提案的一部分。它的名字意味著它是一個提供異步酒觅、順序和迭代訪問其元素的類型撮执。換句話說:它是我們在Swift中熟悉的常規(guī)序列的一個異步變體。
就像你不會經(jīng)常創(chuàng)建你的自定義序列一樣舷丹,我不期望你經(jīng)常創(chuàng)建一個自定義的AsyncSequence
實現(xiàn)抒钱。然而,由于與AsyncThrowingStream和AsyncStream等類型一起使用劫拗,你很可能不得不與異步序列一起工作败匹。因此,我將指導(dǎo)你使用AsyncSequence
實例進行工作伪节。
什么是 AsyncSequence?
AsyncSequence
是我們在Swift中熟悉的Sequence
的一個異步變體瑞信。由于它的異步性厉颤,我們需要使用await
關(guān)鍵字,因為我們要處理的是異步定義的方法凡简。如果你沒有使用過async/await
逼友,我鼓勵你閱讀我的文章:Swift 中的 async/await。
值可以隨著時間的推移而變得可用秤涩,這意味著一個AsyncSequence
在你第一次使用它時可能不包含也可能包含一些帜乞,或者全部的值。
重要的是要理解AsyncSequence
只是一個協(xié)議筐眷。它定義了如何訪問值黎烈,但并不產(chǎn)生或包含值。AsyncSequence
協(xié)議的實現(xiàn)者提供了一個AsyncIterator
匀谣,并負責(zé)開發(fā)和潛在地存儲值照棋。
創(chuàng)建一個自定義的 AsyncSequence
為了更好地理解AsyncSequence
是如何工作的,我將演示一個實現(xiàn)實例武翎。然而烈炭,在定義你的AsyncSequence
的自定義實現(xiàn)時,你可能想用AsyncStream
來代替宝恶,因為它的設(shè)置更方便符隙。因此,這只是一個代碼例子垫毙,以更好地理解AsyncSequence
的工作原理霹疫。
下面的例子沿用了原始提案中的例子,實現(xiàn)了一個計數(shù)器露久。這些值可以立即使用更米,所以對異步序列沒有太大的需求欺栗。然而毫痕,它確實展示了一個異步序列的基本結(jié)構(gòu):
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)
}
}
如您所見,我們定義了一個實現(xiàn) AsyncSequence
協(xié)議的Counter
結(jié)構(gòu)體迟几。該協(xié)議要求我們返回一個自定義的 AsyncIterator
消请,我們使用內(nèi)部類型解決了這個問題。我們可以決定重寫此示例以消除對內(nèi)部類型的需求:
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
}
}
我們現(xiàn)在可以將self
作為迭代器返回类腮,并保持所有邏輯的集中臊泰。
注意,我們必須通過提供typealias來幫助編譯器遵守AsyncSequence
協(xié)議蚜枢。
next()
方法負責(zé)對整體數(shù)值進行迭代缸逃。我們的例子歸結(jié)為提供盡可能多的計數(shù)值针饥,直到我們達到極限。我們通過對Task.isCancelled
的檢查來實現(xiàn)取消支持需频。你可以在這里閱讀更多關(guān)于任務(wù)和取消的信息丁眼。
異步序列的迭代
現(xiàn)在我們知道了什么是AsyncSequence
以及它是如何實現(xiàn)的,現(xiàn)在是時候開始迭代這些值了昭殉。
以上述例子為例苞七,我們可以使用Counter
開始迭代:
for await count in Counter(limit: 5) {
print(count)
}
print("Counter finished")
// Prints:
// 1
// 2
// 3
// 4
// 5
// Counter finished
我們必須使用 await
關(guān)鍵字,因為我們可能會異步接收數(shù)值挪丢。一旦不再有預(yù)期的值蹂风,我們就退出for循環(huán)。異步序列的實現(xiàn)者可以通過在next()
方法中返回nil
來表示達到極限乾蓬。在我們的例子中惠啄,一旦計數(shù)器達到配置的極限,或者迭代取消任内,我們就會達到這個預(yù)期:
mutating func next() async -> Int? {
guard !Task.isCancelled else {
return nil
}
guard current <= limit else {
return nil
}
let result = current
current += 1
return result
}
許多常規(guī)的序列操作符也可用于異步序列礁阁。其結(jié)果是,我們可以以異步的方式執(zhí)行映射和過濾等操作族奢。
例如姥闭,我們可以只對偶數(shù)進行過濾:
for await count in Counter(limit: 5).filter({ $0 % 2 == 0 }) {
print(count)
}
print("Counter finished")
// Prints:
// 2
// 4
// Counter finished
或者我們可以在迭代之前將計數(shù)映射為一個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循環(huán),通過使用contains
等方法越走。
let contains = await Counter(limit: 5).contains(3)
print(contains) // Prints: true
注意棚品,上述方法是異步的,意味著它有可能無休止地等待一個值的存在廊敌,直到底層的AsyncSequence
完成铜跑。
繼續(xù)你的Swift并發(fā)之旅
如果你喜歡你所讀到的關(guān)于異步序列的內(nèi)容,你可能也會喜歡其他的并發(fā)主題:
- Swift 中的 async/await
- Swift 中的 async let
- Swift 中的 Task
- Swift 中的 Actors 使用以如何及防止數(shù)據(jù)競爭
- Swift 中的 MainActor 使用和主線程調(diào)度
- 理解 Swift Actor 隔離關(guān)鍵字:nonisolated 和 isolated
- Swift 中的 Sendable 和 @Sendable 閉包
- Swift 中的 AsyncThrowingStream 和 AsyncStream
- Swift 中的 AsyncSequence
結(jié)論
AsyncSequence
是我們在Swift中熟悉的常規(guī)Sequence
的異步替代品骡澈。就像你不會經(jīng)常自己創(chuàng)建一個自定義Sequence
一樣锅纺,你也不太可能創(chuàng)建自定義的異步序列。相反肋殴,我建議你看一下AsyncStreams囤锉。