LoginSignup
373
326

More than 5 years have passed since last update.

Promiseを複数組み合わせる時の基本パターン(直列、並列、分岐)

Last updated at Posted at 2015-03-25

ES6形式のPromiseを使うときに頻出する3つのパターン。直列パターン、並列パターン、分岐パターンを説明します。

最近、Promise周りが盛り上がっていて、reduceを使ったほうが良いとか、ライブラリがどうとか・・・いう話を聞くのですが、そもそも「ベタに書いたときにどうするのが基本なのか」という情報が見つからないので書いてみました。

直列パターン

一番良く使うのは、複数の処理を直列につなげるパターンでしょう。#1が終わってから、#2、#2が終わってから#3というパターンです。

Promise.resolve()
    .then(function(){
        return new Promise(function(fulfilled, rejected){
            asyncFunc(function(){
                fulfilled();
            });
        })
    })
    .then(function(){
        return new Promise(function(fulfilled, rejected){
            asyncFunc(function(){
                fulfilled();
            });
        })
    })

本来、最初のPromise.resolve()は不要で、new Promiseで始めた方がコードは短くなります。しかし、自分はわざわざコーディングルールとして、Promise.resolve()で始めて、.thenで受けるようにしています。なぜでしょうか?それは、コードのパターンを統一するためです。#1, #2, #3と3つの処理が意味的には対等であるにも関わらず、#1だけ書き方が違うと、コードも見づらいし、処理の順番を変更したとき(あるいは、後述するように分岐や並列化を入れる場合)直感的ではないコード修正が必要になります。このため、上のように最初にダミーのPromise.resolve()を入れるようにしています。

並列パターン

ここに並列処理(Promise.all)を組みわせる場合、以下のようになります。#1と#2を並列に処理し、両方が終わったら#3を行うというパターンです。


Promise.resolve()
    .then(function(){
        return Promise.all([
            new Promise(function(fulfilled, rejected){
                asyncFunc(function(){
                    fulfilled();
                });
            }),
            new Promise(function(fulfilled, rejected){
                asyncFunc(function(){
                    fulfilled();
                });
            })
        ])
    })
    .then(function(){
        return new Promise(function(fulfilled, rejected){
            asyncFunc(function(){
                fulfilled();
            });
        })
    });

並列パターン(map使用)

ただし、上のように複数のPromiseをそのまま書くことはあまり多くなく、以下のようにmapを使ってPromiseを生成することが多いです。

Promise.resolve()
    .then(function(){
        return Promise.all(list.map(function(item){
            return new Promise(function(fulfilled, rejected){
                asyncFunc(function(){
                    fulfilled();
                });
            }),
        }))
    })
    .then(function(){
        return new Promise(function(fulfilled, rejected){
            asyncFunc(function(){
                fulfilled();
            });
        })
    });

分岐パターン

直列パターンで、条件によって処理内容を変えたり、処理をスキップしたいというときに使います。

Promise.resolve()
    .then(function(){
        if(condition1){
            // 条件1 単独のPromise
            return new Promise(function(fulfilled, rejected){
                asyncFunc(function(){
                    fulfilled();
                });
            })
        }else if(condition2){
            // 条件2 Promise2つを直列化
            return Promise.resolve()
                .then(function(){
                    return new Promise(function(fulfilled, rejected){
                        asyncFunc(function(){
                            fulfilled();
                        });
                    })
                })
                .then(function(){
                    return new Promise(function(fulfilled, rejected){
                        asyncFunc(function(){
                            fulfilled();
                        });
                    })
                })
        }else{
            // 条件3 何もしない
        }
    })
    .then(function(){
        return new Promise(function(fulfilled, rejected){
            asyncFunc(function(){
                fulfilled();
            });
        })
    });

ポイントは、.thenの中で、分岐しているということです。もちろん、同じことは、new Promiseの中で分岐させてもできます。ただ、new Promise(fulfilled, rejected){}という塊は、できる限りシンプルな非同期処理の単位であるべきなので、フロー制御は外側に追い出すのが良いと思います。Promiseを返すモジュールを利用する場合も、.thenの中で分岐する習慣にしておくと、コードが綺麗になります。

まとめ

個人的には必ずしも上記のようなパターンを直接使っているわけではなく、モジュール化してもうちょっと便利に使っていたりするのですが、とりあえず直列、並列、分岐という基本パターンは覚えておくべきではないかと思います。

373
326
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
373
326