改良したものをライブラリとして公開しました。
Swiftで複数の非同期処理を扱うライブラリAsyncKitを書きました
非同期処理を並列に実行させてすべてが終わったら別の処理を行う、ということをしたいとき、GCDのgroupを使う方法があります。
ただ、毎回GCDのコードを書くのは美しくないですし、やり方を忘れてしまいがちです。
そこで新しく parallel
という関数を作りました。
JavaScriptの非同期処理を扱うライブラリ "async" にあるparallel関数を参考にしています。
Quick Example
parallel(
[
{ done in done("1") },
{ done in done("2") }
]) { results in
print(results) // -> [Optional(1), Optional(2)]
}
詳しい使い方
非同期処理を行う AsyncProcess
型のクロージャを用意。
let process1: AsyncProcess = { done in
request() { // なんらかの非同期処理
// ...
// 非同期処理が終わったら `done()` を呼ぶ
// `done()` には非同期処理の結果を渡す
done("1")
}
}
// 並列処理を行いたい分だけクロージャを用意。
let process2: AsyncProcess = { done in
request() {
done("2")
}
}
parallel関数にクロージャの配列を渡す。
parallel([process1, process2]) { results in
// すべての非同期処理が終わったらこのクロージャが実行される
// `results` 配列には非同期処理の結果が、parallel関数に渡した非同期処理の順番と同じ順番で入っている
print(results) // -> [Optional(1), Optional(2)]
}
コード
typealias AsyncProcess = (done: AnyObject? -> ()) -> ()
func parallel(acyncProcesses: [AsyncProcess], completion: [AnyObject?] -> ()) {
var results = [AnyObject?](count: acyncProcesses.count, repeatedValue: nil)
let group = dispatch_group_create()
for (i, acyncProcess) in acyncProcesses.enumerate() {
dispatch_group_enter(group)
acyncProcess { result in
results[i] = result
dispatch_group_leave(group)
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
completion(results)
}
}
できないこと
- 結果の型を指定できない
- typealiasでの定義をGenericにできなかったので
- 工夫すればできなくはなさそうですが (Generic typealias in Swift) 、コードのシンプルさを優先しました
- 丁寧なエラーハンドリングができない
- 非同期処理中でエラーが発生した場合
done(nil)
として、results
にnil
が入っているかチェックすればとりあえずエラーハンドリングできます - または、NSError型を渡しても同様にできます
- 理想的には、非同期処理がすべて成功した場合、1つでも失敗した場合で分岐を別けるのが丁寧かと思います
- 非同期処理中でエラーが発生した場合
環境
Apple Swift version 2.1 (swiftlang-700.1.101.6 clang-700.1.76)
Target: x86_64-apple-darwin14.5.0
参考
GCDのgroupについて
http://commandshift.co.uk/blog/2014/03/19/using-dispatch-groups-to-wait-for-multiple-web-services/
JSのasyncライブラリ
https://github.com/caolan/async