PromiseKitの使い方 その3

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

より実践的な応用パターンの書き方

応用パターン

入れ子にする

Promiseの中身は、非同期処理がキューで詰まっている様なものである。 それで、thenの中でPromiseを返す時に、複数の非同期処理を繋いだPromiseを返すことができる

これにより、ある非同期処理の結果によって次に実行する非同期処理のフローを変更するといった使い方ができる

例えば、hogeメソッドの処理結果がtrueなら非同期処理フローA(A-1→A-2→C)を実行、falseなら非同期処理フローB(B→C)を実行させたい場合は、以下の様に実装できる

firstly {
    hoge()
}.then { result: Bool -> Promise<String> in
    if result {
        return self.hogeA()
    } else {
        return self.hogeB()
    }
}.then { result: String 
    // 非同期処理C
}.then {
    // 以下省略
}

func hogeA() -> Promise<String> {
    return firstly {
        // 非同期処理 A-1
    }.then {
        // 非同期処理 A-2
    }
}

func hogeB() -> Promise<String> {
    return firstly {
        // 非同期処理 B
    }
}

例の様に、非同期処理のサブルーチン部分をメソッドに切り出しておくとフローが複雑な場合でもみやすくなり、1つのメソッドが巨大になることも防げる

もちろん、hogeBの様にわざわざメソッドに切り出す程でもないぐらいと判断するなら、

firstly {
    hoge()
}.then { result: Bool -> Promise<String> in
    if result {
        return self.hogeA()
    } else {
        return firstly {
            // 非同期処理 B
        }
    }
}.then { result: String

として、直接入れ子にしても良いが、コールバック地獄が復活する感じなのでおすすめはしない

この時の注意点としては、チェーンにcatchやデータを直接返すthenの様にPromiseを返さないものをつけられないことである。 それでもし、このサブルーチンだけで一旦エラーを処理したい場合(非同期処理フローAの失敗は一旦hogeAの中で処理したい)はrecoverを使うと良い

非同期処理に引数を渡したい場合

Promiseを返すメソッドを作るのに、PromiseKit.wrapは便利だが、引数でデータを渡せないというデメリットがある。かといって、クラスのメンバ変数を使うのはカプセル化が崩れるので望ましくない。そこで、Swiftの関数はファーストクラスであるというのを利用して次の様に書くことができる

func wrapHoge(data: Data) -> Promise<Data> {
    let task = { (completion: (Data?, Error?) -> Void) -> Void in
        hoge(data: data, completion: completion)
    }
    return PromiseKit.wrap(task)
}

// wrapしたいメソッド(dataが引数で渡したいデータ)
func hoge(data: Data, completion: (Data?, Error?) -> Void) {
    // 非同期処理
}

firstly {
    wrapHoge()
}.then { result: Data in

なお、taskの中にhogeメソッドの内容を全て書いてしまうのもあり

非同期処理をスルーしたい場合

例えば、特定の条件の時は非同期処理をまるっと飛ばして(あるいは同期的な処理をして)次の非同期処理に進みたい場合の方法。 非同期処理をせずに次の処理に進むには、Promise(value:)を使う

このコンストラクタは呼び出されると即時に成功扱いとなるPromiseを返す。 引数のvalueには次の処理に渡すデータを指定しなければならない

実際に使う場合は、

firstly {
    hoge()
}.then { result: Bool -> Promise<String> in
    guard result else { return Promise(value:"") }

    return self.hogeA()
}.then { result: String 
    // 非同期処理C

という様に書けば良い。 この例だとhogeの結果がfalseの場合は、""(空文字)のデータを渡すPromiseを返しているので、非同期処理Cに""が渡されて処理が行われる

なお、次の処理(非同期処理C)がVoidの場合は、引数に()を指定すれば良い

開発環境

関連記事一覧