以前書いた記事「Swiftで複数の非同期処理を並列実行させて、すべてが終わったらコールバックを受け取る関数」の反響が思っていたよりあったのでもう少し改良してみることにしました。
そうしたところ、不便だった点が解消され実用に足るものになったと思いますのでライブラリにして公開してみました。
以前の記事では parallel
関数だけだったのですが、それだけではさみしいので series
と waterfall
関数も追加しています。
なにができるのか?
Web API リクエストなどの非同期処理を複数同時に行い、すべてが終わったらそれぞれの非同期処理の結果を使って別の処理をさせたい、ということはよくあると思います。
しかしこれを実装するためには、すべての非同期処理が終わったことを判定する変数を用意したり、GCDを使用したりする必要があり、なかなかきれいにコードを書くことができません。
このライブラリでは、そういった複数の非同期処理をまとめて実行したいときにクリーンで簡潔なコードが書けるような便利な関数を提供します。
特徴としては
- 複数の非同期処理の結果を一か所で受け取れる
- 非同期処理のどれかでエラーが発生したらすぐエラーハンドリングできる (複数の非同期処理でエラーが発生してもエラーハンドリングのコードは1度だけ実行される)
といったことが挙げられます。
ちなみに、このライブラリはJavaScriptの async という有名なライブラリに影響を受けています。
関数名や使い方は async に準拠しています。
AsyncKit
コード
https://github.com/mishimay/AsyncKit/blob/master/Pod/Classes/AsyncKit.swift
インストール
CocoaPodsに登録されています。
use_frameworks!
pod "AsyncKit"
環境
Swift 2.1
簡単なコード例
let async = AsyncKit<String, NSError>()
// 並列実行
async.parallel(
[
{ done in done(.Success("1")) },
{ done in done(.Success("2")) }
]) { result in
print(result) // -> Success(["1", "2"])
}
// 直列実行
async.series(
[
{ done in done(.Success("1")) },
{ done in done(.Success("2")) }
]) { result in
print(result) // -> Success(["1", "2"])
}
// 直列実行 && 非同期処理の結果を次の非同期処理へ渡す
async.waterfall("0"
[
{ argument, done in done(.Success("1")) },
{ argument, done in done(.Success("2")) }
]) { result in
print(result) // -> Success("2")
}
基本的な使い方
- AsyncKitをインスタンス化
-
その際、非同期処理の成功時に結果として渡すオブジェクトの型と失敗時に結果として渡すオブジェクトの型を指定します。
-
e.g.
let async = AsyncKit<String, NSError>()
- 非同期処理の準備
-
非同期処理をクロージャとして用意します。
- クロージャの型
- parallelとseries:
AsyncKit.Process
- waterfall:
AsyncKit.ProcessWithArgument
- parallelとseries:
- クロージャの型
-
上記の「簡単なコード例」のように、AsyncKitの関数を呼ぶ際に配列の中に直接クロージャを書いても構いません。
-
非同期処理の最後に処理が成功した/失敗したことを伝えるため、引数で受け取ったクロージャを呼びます。
- クロージャの引数にはenum
Result
の値 (.Success
か.Failure
) を渡します。 - enumの associated value として、非同期処理の結果として得られたオブジェクトを指定します。
- クロージャの引数にはenum
-
e.g.
let process1: AsyncKit<String, NSError>.Process = { done in request() { object, error in if error == nil { done(.Success(object)) } else { done(.Failure(error)) } } }
- AsyncKitの関数に非同期処理の配列を渡し、結果を受け取る
-
結果はenum
Result
として返され、成功 (.Success
) か失敗 (.Failure
) のどちらかの値になっています。- enumの associated value には、
.Success
時には成功時オブジェクトの配列が、.Failure
時には失敗時オブジェクトが入っています。
- enumの associated value には、
-
e.g.
async.parallel([process1, process2]) { result in switch result { case .Success(let objects): print(objects) case .Failure(let error): print(error) } }
それぞれの関数の説明
parallel
非同期処理を並列実行します。
すべての非同期処理が成功したら .Success
とともに完了クロージャが呼ばれます。
.Success
の associated value は、非同期処理の結果のオブジェクトが非同期処理と同じ順番で入った配列になっています。
いずれかの非同期処理が失敗したら .Failure
とともに完了クロージャが呼ばれます。
複数の非同期処理が失敗しても完了クロージャが呼ばれるのは1度だけです。
series
非同期処理を直列実行します。
すべての非同期処理が成功したら .Success
とともに完了クロージャが呼ばれます。
いずれかの非同期処理が失敗したら .Failure
とともに完了クロージャが呼ばれ、それ以降の非同期処理は実行されません。
waterfall
非同期処理を直列実行し、かつ非同期処理の結果を次の非同期処理へ渡します。
すべての非同期処理が成功したら .Success
とともに完了クロージャが呼ばれます。
いずれかの非同期処理が失敗したら .Failure
とともに完了クロージャが呼ばれ、それ以降の非同期処理は実行されません。
PromiseKitやRxSwiftとの違い
非同期処理を扱うライブラリにはいろいろ種類があり、有名所だとPromiseKitやRxSwiftがあります。
しかしそれらのライブラリは概念が少し複雑で、一度理解すれば便利に使えるのですが最初はとっつきにくい印象があると思います。
また、ライブラリの規模が大きく、ちょっとしたことで使うには少し気後れしてしまいます。
それに対して、このAsyncKitは実態はswiftファイル1つだけなので軽量ですし、やっていることが明瞭なのでなにをするものなのか理解しやすいと思います。
おわりに
以前JavaScriptのライブラリ async を触ったとき、その過不足のない調度良い感じ、手に馴染むその手触り感が好きでした。
それをSwiftでも使いたいなと思ったのが始まりです。
async を使ったことがある方ならすぐに使いこなせると思います。
Swiftの強力な型システムによってより堅牢に、扱いやすくなっていますのでぜひ試してみてください。