PromiseKitの使い方 その2

SwiftでPromiseを使える様になるPromiseKitの使い方 その2

今回はもうちょっと色々なパターンでの使い方のメモ

繋げ方

とりあえず、よく使いそうなものだけ

firstry

一連の非同期処理(チェーン)の最初で使う。なお、糖衣構文なので使わなくても書けるが、通常は使った方が見やすい

then

前の非同期処理の結果が成功であれば次の処理を実行する 

catch

非同期処理の結果が失敗だった場合には、後続のthenは無視されて、一番近いcatchへ飛んでその中の処理が実行される。複数の非同期処理が繋がっていた場合でも、全てのエラーがcatchへ飛んでくる。逆に言うと、どこで発生したエラーかを把握したい場合は、それぞれでエラーを定義しないといけない。発生したErrorはクロージャの引数として受け取れる

チェーンの中にcatchがない場合は、ワーニングが出るので特にエラーを処理しない場合でも空のクロージャとエラー処理不要のコメントをいれておく (ただし、Promiseを返すメソッドで、呼び出し元でエラー処理をしたい場合は不要)

always

チェーンが成功か失敗かに関係なく実行される。例外処理のfinallyと同じ

after

一定時間後に次の(クロージャ内の)処理を実行する

after(seconds:)であれば秒数(TimeInterval)、afterであればDispatchTimeIntervalで経過時間を指定する

内部的には、DispatchQueueasyncAfterを使っているので安心

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 {

(ただし、通常は判りにくくなる可能性があるので、元の処理の中で失敗扱いにできないかをよく考慮すること)

結果として次へ渡せるもの

実は、PromiseKitthenで次の処理へ渡せるのは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 ()と書く)

開発環境

関連記事一覧