Swift AsyncSequence — コード例の詳細な説明

序文

AsyncSequenceConcurrency Framework およびSE-298提案の一部。その名前は、その要素への非同期、順次、および反復アクセスを提供する型であることを暗示しています。言い換えれば、これは Swift でよく知られている通常のシーケンスの非同期バリアントです。

カスタム シーケンスを頻繁に作成しないように、カスタムAsyncSequence実装を。ただし、AsyncThrowingStream和AsyncStreamなどの。したがって、AsyncSequence例を。

ここに画像の説明を挿入

非同期シーケンスとは?

AsyncSequenceSwift でおなじみ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や などLazyMapSequenceAsyncSequence次に、新しいタイプを作成する関数を拡張機能に追加し ('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完了。

結論は

AsyncSequenceSwift でおなじみのSequence通常の。自分でカスタム シーケンスを作成することがあまりないのSequenceと同様に、カスタム非同期シーケンスを作成することもほとんどありません。

おすすめ

転載: blog.csdn.net/qq_36478920/article/details/130521833