Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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の中で分岐する習慣にしておくと、コードが綺麗になります。

まとめ

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした