ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
AsyncSequenceについて
値のシーケンスを反復することは、“for”ループを書くのと同じくらい簡単になりました。新しいAsyncSequenceプロトコルにより、通知をはじめ、サーバからのバイトストリームまで、あらゆるものを反復処理するための自然でシンプルな構文を実現する方法について確認します。また、既存のコードを応用して独自の非同期シーケンスを作成する方法も紹介します。 このセッションを最大限活かしていただくためには、「Swiftのasync/awaitについて」を先にご確認いただくことをお勧めします。
リソース
- SE-0298: Async/Await: Sequences
- SE-0314: AsyncStream and AsyncThrowingStream
- The Swift Programming Language: Concurrency
関連ビデオ
WWDC22
WWDC21
-
ダウンロード
♪ (AsyncSequenceについて) こんにちは Philippeです 今回はSwiftの新機能 AsyncSequenceを ご紹介できてとても嬉しいです 今日は非同期シーケンスとは何かや その根本にあるものについて説明します その後非同期シーケンスのコードでの使い方や 新しいAsynceSequence APIにも触れます 最後には 独自の非同期シーケンスの構築方法を説明します では始めましょう 非常にシンプルなツールです AsyncSequenceで できる素晴らしい新機能をいくつかまとめました このツールではエンドポイントへの URLから始めます 最近のQuake 地震を一覧にしています 何かをダウンロードするのは非同期のタスクで 時間がかかることがあります でもこのケースでは 全部のダウンロードが済むのを待つのではなく 受け取れたものを示したいです そこで少し変えて 新しいasync/await機能を使うことで エンドポイントから返る行を得ることにしました フェッチしていないデータが コンマ区切りでフォーマットされているので 各行がは完全なデータの行です 行のAsyncSequenceは受け取った通りに 各行を出します つまりこの先大量のダウンロードがある可能性が あるということです しかし受け取った時に表示することで とてもレスポンシブになり また一番すごいのは この新しい非同期コンテキストは 通常のシーケンスでいつも使っているのと 同じものを使えることです for-await-in 構文を使った iterate や map filter reduce などの機能 またはこの例のように dropFirst 機能を使って こうした値を操作することができます これはどんな仕組みになっているのでしょうか? 今日お話しすることの多くは async/await を基盤としたものですが 主なポイントをいくつかまとめてみましょう Async機能では コールバックなしで await キーワードを使って 並列コードを書くことができます 非同期関数の呼び出しは停止され 値やエラーが 生成されると再開されます 一方 AsyncSequence では 各要素は停止し 再開は背景にあるイテレータが 値を生成したり エラースロー時に行われます 基本的には名前の通り 通常の Sequence と似ていますが いくつか大きな違いがあります すなわち 各要素が非同期で引き渡されます しかし非同期デリバリーなので 失敗が起こる可能性も必ずあります 非同期シーケンスの中には エラーをスローするものも しないものもあります エラーをスローする関数と同様 コンパイラは反復や構成を行う時に エラーが確実に処理されるようにします 一般的に言うと 非同期シーケンスとは時間の経過とともに 値を生み出す方法を表したものです そのため AppSequence は 0以上値であり イテレータからnilを返すことで完了を示します シーケンスと同様です エラー発生時が発生すると AsyncSequence は終了状態となり エラー発生後の次のイテレータの シーケンス呼び出しで nil を返します では定義の仕組みについて詳しく見ていきましょう まずは通常のイテレーションからです 非常によくあるパターンですね これは for-in ループです このケースではシーケンスから quake を反復し 大きさが一定レベルを超えたときに 関数を呼び出します コンパイラにはイテレーションの 仕組みについて知識がありますが 魔法をかけるわけではありません コンパイルの段階では 単純明快な変換が行われるだけです これらの変換について調べて これの非同期形式が何であるかを 理解できるようにしましょう これは以前のコードのビルドでコンパイラが 行う内容をざっと示したものです まずイテレータ変数の作成から始め whileループを使ってnextが呼び出される際に イテレータが生成するquakeを全て取得します 新しい async/await 機能を使うには ちょっとした微調整で済みます nextの関数を非同期関数に変えるのと 同じように簡単です これで次のquakeを待つことによって反復を Swiftコンカレンシーにすることができます さて少し戻り ループが AsyncSequence に ある場合はどうなるか見ていきましょう 先程述べた通り 非同期シーケンスから 各アイテムを待つ必要があります これは新しい for-await-in 構文に反映されています Sequence の使い方を知っていれば AsyncSequence の使い方も よく分かっているということです 非同期シーケンスを活用するには 幾つかの方法があります 今触れたように 新しい for-await-in 構文を使うか AsyncSequence がエラーをスローする場合は 新しい for-try-await-in 構文を使うかです これで非同期に生成された 値を簡単に反復でき クロージャをいじる必要もなく 既に慣れている構文で 反復を行うことができます break文やcontinue文でもです これで非同期シーケンスの理論を よく理解できたと思います 反復についてもう少し探ってみましょう 非同期シーケンスのソースを与えられたら for-await-in 構文を使って 各値を待つことができます これはイテレータに 生成された各アイテムを保留し 終端に達すると ループを完了します 非同期シーケンスのループ中で 反復を早めに終了するには break を使います これは通常のシーケンスと同じように機能します ここでは quake にロケーションデータがない時に ブレークしています またはスキップしたい値がある場合は continue を使います このケースでは depth がある値より大きい場合 これらをスキップし 続行して次の quake を待ちます ダウンロードからのこの次の反復は 以前と同じように機能しますが このケースでは ソースからエラーがスローされることがあります エラーをスローする関数と同じように 反復中の AsyncSequence のスローは 各要素の処理に try が必要になります またエラーをスローする関数と同じように try が無いとコンパイラが検知し Fix-it を出してミスの訂正を促します これはつまり 非同期シーケンスがエラーを生成する 可能性がある場合 言語がそのエラーを throw するか catch することを強制するため 常に安全であることを意味します 次の反復は最初のループ反復の後 連続的に実行されます コードをシーケンシャルに実行するのが いつも望ましいとは限りません 進行中の他のことと同時に 反復を実行することが有用な場合は 反復をカプセル化する新しい非同期タスクを 作成できます これは使用中の非同期シーケンスが 無制限に実行されると 分かって入る時には便利でしょう さてシーケンスが無制限だったとしても 発生可能性は非常に低いです しかし非同期動作の世界では よく発生しており 使う時にはよく検討する必要があります 有難いことに Swiftのコンカレンシー機能により これはとても簡単かつ安全になっています 反復を外部でキャンセルする 可能性がある時には便利でもあるでしょう こうして2つの反復を並列して実行し 後で反復を終了することができます 無制限かもしれない反復作業を コンテナの寿命にスコープするタスクでは 非常に簡単です では AsyncSequence API をご紹介します これは macOS Monterey・iOS 15・tvOS 15 watchOS 8で利用可能です 沢山ありますが ハイライトだけご紹介します 非同期動作ではファイルからの読み込みが 主なユースケースです FileHandleから非同期シーケンスで読み込むを行う 新しいプロパティ AsyncBytes を FileHandle に追加しました これは非同期シーケンスのバイトを行に変換する AsyncSequenceの新しいExtensionと組み合わせて 使用できます ファイルの処理は非常に一般的であるため URL には bytes と lines の両方の アクセッサを用意しました これは最初の例の APIと同じものです ファイルまたはネットワークのどちらからでも コンテンツから line の AsyncSequence を返す URL のコンビニエンスプロパティです これで以前は非常に複雑だった タスクの多くが簡単で安全になるでしょう ネットワークから取得する時 応答や認証にもっとコントロールが 必要になることがあります そのため URLSession に URL や URLRequest で指定された バイトの AsyncSequence を フェッチする bytes 関数ができました 詳しくは 「URLSessionにおけるasync/awaitの使用」 をご覧ください URLSession の新しい非同期機能について より詳しく学習できます しかし AsyncSequence で意味を成すのは ファイルやネットワークだけではありません 新しい Notification API では 通知も待機できるようになりました また反復以外から AsyncSequence の 利用が可能になりました この例では CoreData の UUID 指定のレコードの リモートでの変更の最初の通知を待機しています firstWhere などのメソッドを AsyncSequence の通知と合わせて使うと 以前は複雑なロジックを実装していたコードを コンパクトで読みやすくすることができ 非常に優れた新しいデザインパターンが 可能になります またこれだけでなく 非同期シーケンスからの値を非同期で操作する 新しいAPIも多く追加されました これはとても親しみやすいと思います その一部は Sequence の機能と 同じものだからです dropFirst や firstWhere など 既にいくつか触れましたが これ以外にもたくさんあります Sequence で使おうと思うもののほとんど全てに AsyncSequence で動かすための 非同期のペアがあります あまりにも多いので 「新しいAPIは確かに素晴らしくて 構文もすっきりしているが 自分の非同期シーケンスはどう作れば良いのか?」 と思われるかもしれません ではやってみましょう AsyncSequence の実装方法はいくつかありますが 既存のコードへの応用方法に フォーカスしたいと思います 特に AsyncSequence とうまく合う デザインパターンがいくつかあり 既存のものをこの新しいコンセプトに 合わせる素晴らしい機能も用意しています そのうちいくつかはクロージャのように 複数回呼び出しされますが 良い感じに動作する delegate もあります 応答を返す必要がなく 発生する新しい値を 伝えるだけなら ほとんどの場合 AsyncSequence 作成のトップ候補になれます これらはよくあるデザインパターンで 皆様のAppの中にももうあるかもしれません これはよくあるハンドラパターンの一例です ハンドラプロパティや Start と Stop メソッドを 持つクラスです AsyncSequence の候補としては完璧に見えますね 既存の使い方はこんな感じでしょうか モニターが作成され 値を取得するハンドラが割り当てられて モニターが開始されるので Quakeをハンドラに送信できます 後に生成中のイベントをキャンセルするため モニターが停止されるかもしれません 同じインターフェイスを使って 新しい AsyncSequence タイプに 応用することができます ほんの少しのコードだけで AsyncSequence を構築可能です AsyncStream を構築する時は 要素タイプと構築クロージャを指定します クロージャは 値を複数回生成 / 終了したり 中断処理を行う continuation を受け取ります このケースでは モニターを構築クロージャ内で作成しています またQuakeを生成するハンドラを continuation に 割り当てることも可能です そしてonTerminationで キャンセルやクリーンアップを 処理できます その後モニタリングを開始します 先程使ったのと同じモニターコードは AsyncStream のコンストラクタ内で 簡単にカプセル化可能です これで同じロジックを使うたびに 複製する必要がなくなります これが AsyncStream の使い方です filter などパワフルな 変換機能や 新しい for-await-in 構文が使えます これによりコーディングに集中でき 簿記の複製に 悩むこともありません 全て1つの場所にまとまっているからです AsyncStream の並外れた柔軟性は 独自の非同期シーケンス作成を可能にします これは1つの例ですが 自分のコードに応用できるケースは 沢山あります AsyncStream では既存のコードを応用して AsyncSequence にすることができます safety や反復放棄など AsyncSequence からと思われるものなら 何でも処理しますが バッファリングの処理もできます AsyncStream は独自の非同期シーケンスや 独自のAPIからの適切な戻り型を 構築するには確実な方法です 生成中される要素のソースが 唯一のコンストラクタだからです ではスローエラーを表示するには? そのための型があります! AsyncThrowingStream は AsyngStream と似ていますが エラーの処理ができます AsyngStream の柔軟性と安全性を備えつつ 反復からスローされるエラーを処理できるのです AsyncSequence は本当にパワフルなツールで 複数の非同期値を 安全かつ簡単に処理することが可能です Sequence の使い方を知っているなら AsyncSequence の使い方は知っているも同然です さて非同期シーケンスの 内容や使い方 AsyncStream の導入方法を説明してきました またその理論や定義を深く掘り下げ 新しく導入された非同期シーケンスにも触れました そして自分で構築する方法も説明しました 皆様が今後どう活用されるか楽しみにしています ♪
-
-
0:37 - QuakesTool
@main struct QuakesTool { static func main() async throws { let endpointURL = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv")! // skip the header line and iterate each one // to extract the magnitude, time, latitude and longitude for try await event in endpointURL.lines.dropFirst() { let values = event.split(separator: ",") let time = values[0] let latitude = values[1] let longitude = values[2] let magnitude = values[4] print("Magnitude \(magnitude) on \(time) at \(latitude) \(longitude)") } } }
-
3:24 - Iterating a Sequence
for quake in quakes { if quake.magnitude > 3 { displaySignificantEarthquake(quake) } }
-
3:52 - How the compiler handles iteration
var iterator = quakes.makeIterator() while let quake = iterator.next() { if quake.magnitude > 3 { displaySignificantEarthquake(quake) } }
-
4:11 - How the compiler handles asynchronous iteration
var iterator = quakes.makeAsyncIterator() while let quake = await iterator.next() { if quake.magnitude > 3 { displaySignificantEarthquake(quake) } }
-
4:28 - Iterating an AsyncSequence
for await quake in quakes { if quake.magnitude > 3 { displaySignificantEarthquake(quake) } }
-
5:36 - Terminating iteration early by breaking
for await quake in quakes { if quake.location == nil { break } if quake.magnitude > 3 { displaySignificantEarthquake(quake) } }
-
5:51 - Skipping values by continuing
for await quake in quakes { if quake.depth > 5 { continue } if quake.magnitude > 3 { displaySignificantEarthquake(quake) } }
-
6:05 - AsyncSequence might throw
do { for try await quake in quakeDownload { ... } } catch { ... }
-
7:15 - Concurrently iterating inside an async task
let iteration1 = Task { for await quake in quakes { ... } } let iteration2 = Task { do { for try await quake in quakeDownload { ... } } catch { ... } } //... later on iteration1.cancel() iteration2.cancel()
-
7:56 - Reading bytes from a FileHandle
for try await line in FileHandle.standardInput.bytes.lines { ... }
-
8:16 - Reading lines from a URL
let url = URL(fileURLWithPath: "/tmp/somefile.txt") for try await line in url.lines { ... }
-
8:49 - Reading bytes from a URLSession
let (bytes, response) = try await URLSession.shared.bytes(from: url) guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 /* OK */ else { throw MyNetworkingError.invalidServerResponse } for try await byte in bytes { ... }
-
9:12 - Notifications
let center = NotificationCenter.default let notification = await center.notifications(named: .NSPersistentStoreRemoteChange).first { $0.userInfo[NSStoreUUIDKey] == storeUUID }
-
11:10 - Using an AsyncStream
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 { ... }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。