PromiseKitの使い方 その2
Nov 20, 2017 · iosswift3
SwiftでPromise
を使える様になるPromiseKitの使い方 その2
今回はもうちょっと色々なパターンでの使い方のメモ
繋げ方
とりあえず、よく使いそうなものだけ
firstry
一連の非同期処理(チェーン)の最初で使う。なお、糖衣構文なので使わなくても書けるが、通常は使った方が見やすい
then
前の非同期処理の結果が成功であれば次の処理を実行する
catch
非同期処理の結果が失敗だった場合には、後続のthen
は無視されて、一番近いcatch
へ飛んでその中の処理が実行される。複数の非同期処理が繋がっていた場合でも、全てのエラーがcatch
へ飛んでくる。逆に言うと、どこで発生したエラーかを把握したい場合は、それぞれでエラーを定義しないといけない。発生したError
はクロージャの引数として受け取れる
チェーンの中にcatch
がない場合は、ワーニングが出るので特にエラーを処理しない場合でも空のクロージャとエラー処理不要のコメントをいれておく
(ただし、Promise
を返すメソッドで、呼び出し元でエラー処理をしたい場合は不要)
always
チェーンが成功か失敗かに関係なく実行される。例外処理のfinally
と同じ
after
一定時間後に次の(クロージャ内の)処理を実行する
after(seconds:)
であれば秒数(TimeInterval
)、after
であればDispatchTimeInterval
で経過時間を指定する
内部的には、DispatchQueue
のasyncAfter
を使っているので安心
when
配列で渡されたPromise
全てを並列で実行し全てが成功すればthen
が呼ばれる。ライブラリによってはall
と呼ばれるものと同じ
失敗したものが1つでもあれば、catch
が呼ばれる。ただし、実行中の非同期処理自体がキャンセルされるわけではないので注意
recover
通常、失敗した場合はcatch
へ飛ばされてしまって、以降の処理を継続することはできないが、エラーの内容によってはリカバリ処理を行うなどして処理を継続させたいといった時に使う
発生したエラーはcatch
と同じく引数として受け取れるので、それを元に判断して復帰させられるなら結果を返し、やはり失敗のままにする場合はエラーを再送すれば良い
firstry {
hoge1()
}.recover { error -> Promise<T> in
guard error is UnknownError {
throw error
}
return self.hoge2()
}
このコードの場合だと、hoge1
で発生したエラーを一旦捕まえて、UnknownError
かどうかを判定し、UnknownError
であれば、再度throw
してエラー処理へ回し、それ以外なら別の非同期処理を実行させている
なお、単にエラー処理の選別だけでなく、例えばネットワーク経由で最新値を取得、失敗した場合に圏外のエラーならデフォルト値を返すといった用途にも使える
小ネタ
成功で返ってきた後に失敗扱いにしたい場合
例えば、then
で成功時の結果を受け取ったものの、それを失敗扱いにしてcatch
へ飛ばしたい場合は、例外をthrow
すれば良い
firstry {
hoge1()
}.then { (result: Data) -> Promise<T> in
guard !result.isEmpty() else {
throw EmptyError()
}
return self.hoge2()
}.then {
(ただし、通常は判りにくくなる可能性があるので、元の処理の中で失敗扱いにできないかをよく考慮すること)
結果として次へ渡せるもの
実は、PromiseKit
のthen
で次の処理へ渡せるのはPromise
だけではなく、任意の型のデータを渡すことができる。
また、チェーンの途中で渡す型を変えることもできる(最初はPromise<String>
で次にPromise<Data>
、最後はPromise<String>
を渡すといったことが可能)
いずれにしても、then
で返す型と次のthen
が受け取る型が一致していないとビルドエラーになる
これにより、あまりないかもしれないが、クロージャ内の処理が非同期でない場合は、直接そのデータを返すことが可能である。例えば、非同期のhoge1
を実行し結果のデータを処理してから、次の非同期処理を行いたいといった場合は、
firstry {
hoge1() // ここは非同期なのでPromise<Data>を返している
}.then { (result: Data) -> String in
// ここでresultを文字列へ変換
guard let str = String(data: result, encoding: .utf8) {
throw EncodeError()
}
return str
}.then { (text: String) -> Promise<T> in
// 次の非同期処理
}
の様にすれば良い。通常はhoge1
の中で文字列まで変換して処理すれば良いが、共通化されたメソッドでそうできなかったり、設計上そうすべきでない様な場合には役立つ
また、recover
で出てきたデフォルト値を直接返す場合にも有効
Promise<Void>
後続のthen
が呼ばれる=前回の非同期処理は成功したことになるので、結果として何もデータを渡す必要がない場合がある。この場合は、Promise<Void>
の型を使えば良い
Promise
を返す側(非同期処理)では、何も返さなければOK(return
を書かない)。もし、どうしてもreturn
を書いたり、引数としてVoid
を渡す必要があるなら、()
と書けばVoid
扱いとなる(return
ならreturn ()
と書く)
開発環境
- Xcode 8.3.3
- iOS 10.3
- PromiseKit 4.4