オープニング
async/await について
- 2017年のChris Lattner氏のマニフェストにて提唱される
- 結局それに手がつけられたのは2020年末
- ロードマップが2020年10月頃発表、そこから各機能のPitchなどが始まる
- WWDC2021でSwift5.5の新機能として発表され、 Xcode13 Betaにて登場。しかし現在(2021年6月段階)でもレビュー段階のままなので今後も変わる可能性
- WWDC2021のセッションから変更する予定のものもあって注意が必要 (例えば、Structured Concurrency といった機能もシンタックスが変わる)
- Swift6に向けてどうするか、という話がSwift Forumsにあがっている
Swift Concurrency が解決する問題
- クロージャ特有の問題
- DispatchQueueを利用する際のクロージャによるネストの深さがなくなる
- completionHandlerの問題が解決される
- completionHandler 呼び忘れ
- completionHandler の後の return 呼び忘れ
- Race Conditionを防ぐことができる
- いままでDispatchQueueでやっていたところがactorに代わってくれる
- 型レベルでのデータの保護
- いままでSwiftを書く人が頑張ってたところが、コンパイラがサポートしてくれるようになる
async/await に代わっていくコード
実際に読むコード
https://developer.apple.com/videos/play/wwdc2021/10132/ より抜粋
func fetchThumbnail(for id: String, completion: @escaping (UIImage?, Error?) -> Void) {
let request = thumbnailURLRequest(for: id)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(nil, error)
} else if (response as? HTTPURLResponse)?.statusCode != 200 {
completion(nil, FetchError.badID)
} else {
guard let image = UIImage(data: data!) else {
completion(nil, FetchError.badImage)
return
}
image.prepareThumbnail(of: CGSize(width: 40, height: 40)) { thumbnail in
guard let thumbnail = thumbnail else {
completion(nil, FetchError.badImage)
return
}
completion(thumbnail, nil)
}
}
}
task.resume()
}
func fetchThumbnail(for id: String) async throws -> UIImage {
let request = thumbnailURLRequest(for: id)
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw FetchError.badID }
let maybeImage = UIImage(data: data)
guard let thumbnail = await maybeImage?.thumbnail else { throw FetchError.badImage }
return thumbnail
}
実際に見ていてどうか?
- async/await 有り のほうが圧倒的に行数が少ないし、ネストが深くない
- async/await 有り は純粋に純粋に上から下に処理が流れる
- async/await 無しは古いURLSessionなので、resume()関数を呼ばなければならない
- async/await という単語がところどころにある。単語を書く順番も決まっている
- URLSessionのdataTask関数を呼んで結果が帰ってきたときの挙動としてクロージャを定義するスタイルから、URLSessionのdata関数を呼んでそのまま結果をawaitして得るスタイルへ
- dataTaskのクロージャの引数は3つ(Data, Responce, Error)はすべてOptional型なので、自分でBindingしてエラーハンドルしないといけない
- dataは(Data, Responce)はnon-Optional型、 エラーはtry越しで返ってくる
- dataTaskのクロージャの中でDispatchQueueをまた読んでネストが深くなる・・・ということもdataではなくなる
- データや処理の流れもひと目見てわかりやすい
async/await を学ぶ際に・・・
async/await(Swift Concurrency) はiOS15のみ
Xcode 13の最初のBetaのRelease Noteだと known issue になってるけど、公式曰くミス
公式にiOS14以下が切られる時、もしくはアプリで(ビジネス的に許されるなら)iOS14を切るという対応も一つの手
if avaibaleなどで分岐してもいいけどメンテナンスコスト高くなりそう
- (2021/Sep 追記) Back Deployment のPRがマージされました https://github.com/apple/swift/pull/39051
- 初心者がasync/awaitを学ぶには?
- WWDC2021のセッションは豊富なので見てほしい
- DispatchQueueへのリプレイスのための勉強はそこまで苦ではない
- 初心者がそもそも非同期という概念を学ぶには?
- 英語への抵抗
- ちょっとしたshinzanさん的まとめ(学ぶおすすめの順番)
- async/await → 非同期処理を同期的な処理と同じような方法で、簡潔にわかりやすく、ミス少なく書くことができる
- Structured Concurrency → async/awaitを使って同時並行(concurrency)処理をよりわかりやすく(内部的に)効率よく、扱うことができる
- actor → 同時並行(concurrency)処理の中でもデータ競合(複数スレッドから同じデータを変更しようとすること)を防ぐことができる
- https://developer.apple.com/videos/play/wwdc2021/10194/ もおすすめ
- サンプルアプリをステップ・バイ・ステップでasync/awaitに対応する というセッション
エンディング