ハイライト
AsyncSequenceSwift 5.5で導入されたコアプロトコルであり、同期ですSequenceの非同期同等物。のようにfor item in sequence同期シーケンスをたどるのと同じように、for await item in asyncSequence非同期的に生成された要素を反復処理することができます。
主な内容
存在するasync/await以前は、一連の非同期値を処理するには、コールバック、プロキシ パターン、または Combine Publisher が必要でした。これらのアプローチにはそれぞれ独自の複雑さがあり、ネストされたコールバックは「コールバック地獄」を引き起こし、エージェントは複数のメソッドに分散しており、Combine には急な学習曲線があります。
AsyncSequence最も単純なものを使用するfor-await-inループ処理はこの問題を解決します。これにより、非同期シーケンスの処理が同期シーケンスとほぼ同じように見えます。コンパイラは舞台裏で次のように動作します。for-await-inペアに変換するmakeAsyncIterator()電話をかけ、それを繰り返しますawait iterator.next()。
システムフレームワークのAPIが多数提供されているAsyncSequenceインタフェース。URL.linesテキスト ファイルは 1 行ずつ非同期で読み取ることができます。URLSessionのbytes(from:)戻るAsyncBytes。NotificationCenterのnotifications(named:)通知シーケンスを返します。FileHandle非同期読み取りもサポートされています。
セッションでも紹介していますAsyncStreamそしてAsyncThrowingStream、従来のコールバック/デリゲート パターンを次のように変換します。AsyncSequence橋。使用できますAsyncStreamTimer と CLLocationManager のデリゲート コールバックをラップします。AsyncSequence。
##詳細
AsyncSequence のトラバース
(04:28)
for await quake in quakes {
if quake.magnitude > 3 {
displaySignificantEarthquake(quake)
}
}
キーポイント:
for awaitSwift 5.5 のトラバース用の新しい構文ですAsyncSequence- シーケンスでエラーが発生する可能性がある場合は、次を使用します。
for try await breakそしてcontinue存在するfor-await-in正常に動作しています
for try await quake in quakeDownload {
if quake.location == nil {
break
}
if quake.magnitude > 3 {
displaySignificantEarthquake(quake)
}
}
コンパイラーが非同期反復を処理する方法
(03:52)
// 同步迭代
var iterator = quakes.makeIterator()
while let quake = iterator.next() {
if quake.magnitude > 3 {
displaySignificantEarthquake(quake)
}
}
// 异步迭代
var iterator = quakes.makeAsyncIterator()
while let quake = await iterator.next() {
if quake.magnitude > 3 {
displaySignificantEarthquake(quake)
}
}
キーポイント:
- コンパイラ
for-inに変換while let+makeIterator()+next() - コンパイラ
for await-inに変換while let await+makeAsyncIterator()+next() - 各反復がハングし、次の値の到着を待機する可能性があります
同時反復
(07:15)
let iteration1 = Task {
for await quake in quakes {
displayEarthquake(quake)
}
}
let iteration2 = Task {
do {
for try await quake in quakeDownload {
updateMap(with: quake)
}
} catch {
handleError(error)
}
}
// 后续可以取消
iteration1.cancel()
iteration2.cancel()
キーポイント:
- バンドル
for-await-inつけるTaskクロージャ内で複数の反復を同時に実行可能 Task提供されたcancel()メソッド、反復は外部からキャンセル可能- 無限に実行される可能性のあるシーケンス (通知フローなど) の場合、この方法でライフ サイクルを管理できます。
FileHandle からバイトを読み取ります
(07:56)
for try await line in FileHandle.standardInput.bytes.lines {
processLine(line)
}
キーポイント:
FileHandle.bytes戻るAsyncSequence<UInt8>.lines拡張機能はバイトストリームを文字列ストリームに変換し、行ごとに分割します- 標準入力、パイプライン データ、その他のストリーミング シナリオの処理に適しています
URLからファイルを読み取ります
(08:16)
let url = URL(fileURLWithPath: "/tmp/somefile.txt")
for try await line in url.lines {
print(line)
}
キーポイント:
URL.linesSwift 5.5で追加された新しい便利な属性です- ファイルのオープン、クローズ、エラーを自動的に処理します
- ファイル全体を一度にメモリにロードせずに、1 行ずつ読み取ります。
URLSession からバイト ストリームを取得する
(08:49)
let (bytes, response) = try await URLSession.shared.bytes(from: url)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw MyNetworkingError.invalidServerResponse
}
for try await byte in bytes {
processByte(byte)
}
キーポイント:
bytes(from:)戻る(AsyncBytes, URLResponse)タプルAsyncBytesはいAsyncSequence<UInt8>- バイト単位の処理。ストリーミング解析や大きなファイルのダウンロードに適しています。
特定の通知を待ちます
(09:12)
let center = NotificationCenter.default
let notification = await center.notifications(named: .NSPersistentStoreRemoteChange).first {
$0.userInfo[NSStoreUUIDKey] == storeUUID
}
キーポイント:
notifications(named:)戻るAsyncSequence<Notification>- 組み合わせる
first(where:)条件を満たす最初の通知を待つことができます - これは、デリゲート コールバックを実装したり、オブザーバーを追加したりするよりもクリーンです
AsyncStream の使用
(11:10)
class QuakeMonitor {
var quakeHandler: (Quake) -> Void
func startMonitoring()
func stopMonitoring()
}
let quakes = AsyncStream(Quake.self) { continuation in
let monitor = QuakeMonitor()
monitor.quakeHandler = { quake in
continuation.yield(quake)
}
continuation.onTermination = { @Sendable _ in
monitor.stopMonitoring()
}
monitor.startMonitoring()
}
let significantQuakes = quakes.filter { quake in
quake.magnitude > 3
}
for await quake in significantQuakes {
displaySignificantEarthquake(quake)
}
キーポイント:
AsyncStreamコンストラクターはクロージャーを受け入れます。continuation値を生成するために使用されるcontinuation.yield()シーケンスに値をポストするcontinuation.onTerminationリソースをクリーンアップするためにシーケンスがキャンセルされるときに呼び出されますAsyncStream一緒にいることができますfilter、map他の演算子と組み合わせて使用する
AsyncStream 処理エラー
エラーをスローする必要がある場合は、次を使用しますAsyncThrowingStream:
let events = AsyncThrowingStream(Event.self) { continuation in
let monitor = EventMonitor()
monitor.eventHandler = { result in
switch result {
case .success(let event):
continuation.yield(event)
case .failure(let error):
continuation.finish(throwing: error)
}
}
monitor.start()
}
キーポイント:
AsyncThrowingStreamのcontinuation.finish(throwing:)エラーを報告するため- 反復処理時に必要
for try await - エラーが発生するとシーケンスが終了します。
next()コールリターンnil
重要ポイント
1. URL.lines を使用して大きなファイルを処理する
アプリで大きなテキスト ファイル (ログ、CSV、JSON 行) を処理する必要がある場合は、それらを一度にメモリに読み込まないでください。使用url.lines行ごとに処理され、メモリ使用量は一定です。
実装アイデア: 置き換えるString(contentsOfFile:)のためにfor try await line in url.lines。ループ内で 1 行ずつ解析し、必要な情報を抽出して破棄します。
2. AsyncStream を使用してタイマー コールバックをラップする
伝統的Timer.scheduledTimerこれはターゲット メソッドで処理する必要があるため、ロジックの邪魔になります。使用AsyncStreamラップ後、すべての時間指定イベントをループで処理できます。
実装アイデア: 作成するAsyncStream、存在するTimerコールバックで呼び出されるcontinuation.yield(())。次に、使用しますfor await _ in timerStreamスケジュールされたタスクを処理します。
3. AsyncStream を使用して、NotificationCenter コールバックをラップします。
一部の通知は、特定の条件によってトリガーする必要があります。従来の方法では、最初にオブザーバーを追加してから、コールバックで条件を判断する必要があります。使用notifications(named:)のfirst(where:)条件を満たす通知を直接待つことができます。
実装アイデア: 置き換えるaddObserver(forName:object:queue:using:)のためにawait center.notifications(named:).first { 条件 }。コードがよりクリーンになり、意図がより明確になります。
4. AsyncStream を使用してネットワーク ページングの読み込みをラップする
Web API のページ読み込みでは、多くの場合、次のページへの再帰呼び出しが必要になります。使用AsyncStreamページング シーケンスを線形反復に変えることができます。
実装アイデア: 作成するAsyncStream、非同期クロージャの最初のページをロードします。continuation.yield各データを返し、次のページを再帰的に読み込みます。発信者が行う必要があるのは、for await item in paginatedStream。
5. AsyncStream を使用して CoreLocation コールバックをラップする
CLLocationManagerデリゲート コールバックは複数のメソッドに分散されます。使用AsyncStream位置情報の更新は均一に処理できます。
実装アイデア: 必要なイベントごとに作成AsyncStream(のようにdidUpdateLocations、didFailWithError)、デリゲート メソッドで対応するメソッドを呼び出します。continuation.yield。
関連セッション
- Meet async/await in Swift — Swift 5.5 で導入された async/await 構文の詳細な説明
- Swift で構造化同時実行性を探る — Swift 構造化同時実行性モデル
- SwiftUI で同時実行性を確認する — SwiftUI で同時実行機能を使用する
- Swift アクターを使用して可変状態を保護する — アクターを使用してデータ競合を回避します
コメント
GitHub Issues · utterances