非同期をわかりやすく制御しよう!ES6の「Promise」入門

非同期をわかりやすく制御しよう!ES6の「Promise」入門

先生

先生

Promiseを使って同期的に処理する

Promiseを使って複数の非同期処理を順番に制御してみましょう。

function func1(num) {
    return new Promise(function(resolve,reject) {
        window.setTimeout(function() {
            resolve(num);
        },1000);
    });
}
function func2(num) {
    return new Promise(function(resolve,reject) {
        window.setTimeout(function() {
            resolve(num*num);
        },1000);
    });
}

func1(2)
    .then(func2)
    .then(function(result) {
        console.log(result);
    })
    .catch(e) {
        console.error(e);
    });

1444963921693

動作確認

func1の処理の完了を待ち、処理結果をfunc2で受けてデータを返すという形で順番に処理が実行されます。コードとしても順番に処理されているのがわかりやすくなっています。

これを従来のコードで書いた場合には、下記のようになります。

function func1(num,callback,onFailed) {
    window.setTimeout(function() {
        callback(num);
    },1000);
}
function func2(num,callback,onFailed) {
    window.setTimeout(function() {
        callback(num*num);
    },1000);
}

func1(2, function(num) {
    func2(num, function(result) {
        console.log(result);
    }, function(e) {
        console.error(e);
    });
}, function(e) {
    console.error(e);
});

このようにするとfuncがどんどん入れ子になってインデントが深くなっていきます。エラーの処理もしにくくなり読みにくくなっていますね。まだ2つだから良いですが、これが5つ6つとなっていくとメンテナンス性はさらに悪くなっていきます。

func1(2, function(num) {
    func2(num, function(num) {
        func3(num, function(num) {
            func4(num, function(num) {
                func5(num, function(num) {
                    console.log(num);
                });
            }); 
        });
    });
});

F

これをPromiseを用いた場合は、下記のようにすっきりとさせることが可能です。

func1(2)
    .then(func2)
    .then(func3)
    .then(func4)
    .then(func5)
    .then(function(num) {
        console.log(num);
    });

どちらが処理を追いやすいかは一目瞭然ですね。

Promiseを使って並列処理して完了を検知する

続いて、複数の非同期処理を並列で実行し、すべてが完了したら情報を取得する処理をしたいと思います。これが非同期のメリットですものね。

function func1(num) {
    return new Promise(function(resolve,reject) {
        window.setTimeout(function() {
            resolve(num+num);
        },500);
    });
}
function func2(num) {
    return new Promise(function(resolve,reject) {
        window.setTimeout(function() {
            resolve(num*num);
        },2000);
    });
}

Promise.all([func1(3),func2(3)]).then(function(results) {
    console.log(results);
}).catch(function(e) {
    console.log(e);
});

1444964780197

動作確認

Promise.all([Promise,Promise,...])を使って非同期な処理を並列でおこない、すべてがresolveになるタイミングをthenで取得します。

並列で処理しているものが1つでもrejectとなった場合はcatchが実行されます。

function func1(num) {
	return new Promise(function(resolve,reject) {
		window.setTimeout(function() {
			resolve(num+num);
		},500);
	});
}
function func2(num) {
	return new Promise(function(resolve,reject) {
		window.setTimeout(function() {
			reject(num*num);
		},2000);
	});
}

Promise.all([func1(3),func2(3)]).then(function(results) {
	console.log(results);
}).catch(function(e) {
	console.log(e);
});

1444964998108

動作確認

従来は完了数をカウントして全部終わったかどうかを確認するなどしていましたが、とても簡単に並列処理の完了を捕捉することができます。

1個でも処理が返ってきたら実行する

続いて少し用途は狭くなりますが、並列で処理しているものが1つでもresolveになったら実行する処理を作りたいと思います。

function func1(num) {
    return new Promise(function(resolve,reject) {
        window.setTimeout(function() {
            resolve(num+num);
        },500);
    });
}
function func2(num) {
    return new Promise(function(resolve,reject) {
        window.setTimeout(function() {
            reject(num*num);
        },2000);
    });
}

Promise.race([func1(3),func2(3)]).then(function(results) {
  console.log(results);
}).catch(function(e) {
  console.log(e);
});

1444974524299

動作確認

Promise.race([Promise,Promise,...])を使うことで並列で実行した処理でもっとも早く結果を返してきたらthenやcatchが実行されるようになります。もし最初に結果を返してきたものがrejectであれば、catchが実行されます。

使いどころはやや難しいですね。

まとめ

このようにPromiseを使うと非同期の処理をとても制御しやすくなります。とくに、Node.jsでは非同期処理が多いので積極的にPromiseを活用して制御していきましょう。

なお、本稿ではPromiseesというPromiseの挙動を可視化するツールを用いて紹介しました。Promiseの処理が複雑になってしまったときの活用や動作フローの確認などに使えるので、積極的に活用してみてください。

それでは、また。

LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。

Webサイト制作の実績・料金を見る

この記事のシェア数

CTOの林です。フロントエンドを専門とし、AngularJSのコミュニティをはじめ、様々な勉強会に顔を出しています。効率化マニアでGrunt,Gulpをはじめ、プロジェクト進行やサーバーサイド、インフラ周りの効率化を目指し日々活動しています。 【役職紹介 / CTO】 Web制作にかかる内容を統括しています。プロジェクトを横断的に見渡し、技術的な見地からアドバイスなどを行い、ときには実制作のヘルプを行ったりしています。新人エンジニアの育成や、体外的な活動としてLIG全体のエンジニアのブランディングのため勉強会やイベントへの登壇・開催なども行っています。 【普段やっていること】 新規サービスの設計(UI,遷移,DB,APIなど)、エンジニアの育成及び環境整備、技術選定、Web制作ユニットの統括とプロジェクト進行を円滑にするための仕組みづくり、フロントエンド周りの実装、エンジニアリングの開発効率化、勉強会の登壇・主催など

このメンバーの記事をもっと読む
ES6のある星に生まれて | 8 articles
デザイン力×グローバルな開発体制でDXをトータル支援
お問い合わせ 会社概要DL