記事ディレクトリ
序文
AsyncSequence
Concurrency Framework およびSE-298提案の一部。その名前は、その要素への非同期、順次、および反復アクセスを提供する型であることを暗示しています。言い換えれば、これは Swift でよく知られている通常のシーケンスの非同期バリアントです。
カスタム シーケンスを頻繁に作成しないように、カスタムAsyncSequence
実装を。ただし、AsyncThrowingStream和AsyncStream
などの。したがって、AsyncSequence
例を。
非同期シーケンスとは?
AsyncSequence
Swift でおなじみSequence
の。非同期の性質上、非同期で定義されたメソッドを扱っているため、await
キーワードを。まだ使用していない場合はasync/await
、私の記事を読むことをお勧めします: async/await in Swift - コード例の説明
値は時間の経過とともに使用可能になる可能性があります。つまり、AsyncSequence
値を最初に使用したときに、一部またはすべての値が含まれている場合と含まれていない場合があります。
これはAsyncSequence
単なるプロトコルであることを。値にアクセスする方法を定義しますが、値を生成したり格納したりしません。AsyncSequence
プロトコルの実装者は 1 つを提供しAsyncIterator
、値を開発し、場合によっては格納する責任があります。
関数 | ノート |
---|---|
contains(_ value: Element) async rethrows -> Bool |
Equatable 要素が必要です |
contains(where: (Element) async throws -> Bool) async rethrows -> Bool |
クロージャーの は、async オプションの非同期動作を許可しますが、必須ではありません |
allSatisfy(_ predicate: (Element) async throws -> Bool) async rethrows -> Bool |
|
first(where: (Element) async throws -> Bool) async rethrows -> Element? |
|
min() async rethrows -> Element? |
Comparable 要素が必要です |
min(by: (Element, Element) async throws -> Bool) async rethrows -> Element? |
|
max() async rethrows -> Element? |
Comparable 要素が必要です |
max(by: (Element, Element) async throws -> Bool) async rethrows -> Element? |
|
reduce<T>(_ initialResult: T, _ nextPartialResult: (T, Element) async throws -> T) async rethrows -> T |
|
reduce<T>(into initialResult: T, _ updateAccumulatingResult: (inout T, Element) async throws -> ()) async rethrows -> T |
これらの関数では、最初にAsyncSequence
プロトコル。名前は、LazyDropWhileCollection
や などLazyMapSequence
。AsyncSequence
次に、新しいタイプを作成する関数を拡張機能に追加し ('self' を 'upstream' として使用)、それを返します。
関数 |
---|
map<T>(_ transform: (Element) async throws -> T) -> AsyncMapSequence |
compactMap<T>(_ transform: (Element) async throws -> T?) -> AsyncCompactMapSequence |
flatMap<SegmentOfResult: AsyncSequence>(_ transform: (Element) async throws -> SegmentOfResult) async rethrows -> AsyncFlatMapSequence |
drop(while: (Element) async throws -> Bool) async rethrows -> AsyncDropWhileSequence |
dropFirst(_ n: Int) async rethrows -> AsyncDropFirstSequence |
prefix(while: (Element) async throws -> Bool) async rethrows -> AsyncPrefixWhileSequence |
prefix(_ n: Int) async rethrows -> AsyncPrefixSequence |
filter(_ predicate: (Element) async throws -> Bool) async rethrows -> AsyncFilterSequence |
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
または、反復する前にカウントを 1 にマップできます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
と同様に、カスタム非同期シーケンスを作成することもほとんどありません。