こんにちは、ライターのマサトです!今回は、Node.jsで非同期処理を効率的に記述することができる「Promise」について学習をしていきましょう!
この記事では、
- 「Promise」とは?
- 「Promise」の使い方
- 「Promise」の並列処理
- エラーハンドリングについて
というように、基本的な内容から応用的な使い方に関しても解説していきます。この記事で、Promiseをしっかり学習して自分のスキルアップを目指しましょう!
「Promise」とは?
それでは、まず最初にPromiseについて基本的な知識から身に付けていきましょう!Promiseは従来のfunctionを使った非同期処理よりも、コードの見通しが良くてエラーハンドリングなども書きやすい方法になります。
例えば、「コールバック地獄」と呼ばれる次のようなコードを知っている方も多いでしょう。
getDate(function(data1) { getYear(function(data2) { getSomething(function(data3) { getAnotherThing(function(data4) { //何らかの処理をする }); }); }); });
これは非同期処理を何重にも内包してしまい、コードの見通しが悪くなるばかりかデバッグも困難な状況になってしまいます。
まったく同じコードをPromiseを使えば次のようになります!
getDate() .then(function(data) { return getYear(data) }) .then(function(year) { return getSomething(year) }) .then(function(item) { getAnotherThing(item) })
コードが見やすくなったうえ、それぞれの非同期処理においてエラーハンドリングも実行しやすくなっているのが特徴です。それでは、なぜPromiseだとこのような記述ができるのか、次の章から詳しく見ていきましょう。
「Promise」の使い方
この章では、基本的なPromiseの使い方について見ていきましょう!主に、一般的な構文やPromise処理の作り方、メソッドチェーンについて学んでいきます。
基本的な構文と書き方について
まずは、Promiseの基本となる書き方から見ていきましょう。Promise処理を作るには任意の関数内で「new Promise()」をreturnするというのが基本になります。
例えば、次のように記述することができますね!
return new Promise(resolve) { //ここに処理を記述する }
このようにPromiseをreturnする処理を、関数の中に記述することで非同期処理を作ることができるわけです。
※エラーハンドリングについては後述します。
Promise処理の作り方
それでは、実際に簡単なPromise処理を作ってみましょう!今回は単純に「Hello Promise」という文字列を返すだけの処理を作ってみます。次のサンプル例を見てください!
function myPromise() { return new Promise(function (resolve) { resolve('Hello Promise'); }) }
この例では、前章でも解説したように関数内にPromiseをreturnする処理を記述していますね。関数の引数である「resolve()」に返したい結果となる値を指定すればいいので、今回は文字列を設定しています。
あとは、次章で解説する方法でPromise処理を呼び出せば非同期処理の結果を取得することができます。
thenによるメソッドチェーンのやり方
Promise処理が記述された関数の呼び出し方について見ていきましょう。Promiseによる非同期処理の結果を取得するには「then()」を使ったメソッドチェーンを利用することができます。
例えば、先ほど作成した「myPromise()」の結果を取得するには次のように記述します!
myPromise() .then(function(data) { console.log(data) })
実行結果
Hello Promise
「then()」の中で関数を記述して、引数の「data」にPromiseの結果が格納されています。このPromiseの結果というのは、resolve()に設定した文字列のことなので実行結果には指定した文字列が出力されるというわけです。
ちなみに、Promise処理の特徴としてthen()をいくつも繋げて実行していくことができます。
次のサンプル例を見てください!
myPromise1() .then(function(data1) { console.log(data1); return myPromise2(); }) .then(function(data2) { console.log(data2); return myPromise3(); }) .then(function(data3) { console.log(data3); })
このようにthen()の中で最後に別のPromise処理をreturnすれば、続けてthen()による処理が可能になるわけです。これで逐次処理も簡単に実現できますね。
「Promise」の並列処理
この章では、複数のPromise処理を並列に実行する方法について見ていきましょう!主に、all() / race()というメソッドを使った手法について学んでいきます。
「all()」を使った並列処理
まずは、並列処理の定番である「all()」について見ていきましょう!all()は、異なるPromise処理が記述された関数をまとめて実行し、すべての結果が得られたタイミングでthen()を実行できるようになります。
つまり、複数のPromise処理の結果をまとめて取得したい場合に便利なメソッドと言えるでしょう。
そこで、以下のような時間のかかるPromise処理を作って確認してみましょう!
function myPromise1() { return new Promise(function (resolve) { setTimeout(function() { resolve('Hello Promise1'); }, 3000) }) } function myPromise2() { return new Promise(function (resolve) { setTimeout(function() { resolve('Hello Promise2'); }, 1000) }) } function myPromise3() { return new Promise(function (resolve) { setTimeout(function() { resolve('Hello Promise3'); }, 5000) }) }
この例では、「setTimeout()」を使ってわざと時間のかかる処理を作成しています。異なる時間を設定したPromise処理を3種類作っているのが分かりますね。
この3つのPromise処理を並列にまとめて実行するには次のように記述します!
Promise.all([ myPromise1(), myPromise2(), myPromise3() ]) .then(function(data) { console.log(data); })
実行結果
["Hello Promise1", "Hello Promise2", "Hello Promise3"]
all()に実行したいPromise処理を配列で指定するのが基本になります。あとは、続けてthen()による結果を取得すれば完成ですね。
実行結果を見るとわかりますが、すべてのPromise処理が実行されてから結果を得られています。
「race()」を使った並列処理
次に、類似メソッドとしてrace()による並列処理を見ていきましょう!race()も複数のPromise処理を実行できるのですが、最初に結果が得られたPromiseの結果だけをthen()で取得することができます。
前回とまったく同じPromiseを並列で実行してみましょう!
Promise.race([ myPromise1(), myPromise2(), myPromise3() ]) .then(function(data) { console.log(data); })
実行結果
Hello Promise2
この例では、3つのPromiseを実行していますが最も速く結果を得られるのはmyPromise2()ですね。そのため、then()による実行結果はmyPromise2()で設定した文字列だけが取得できているというわけです。
ただし、他のPromise処理も引き続き実行はされているという点に注意しましょう。あくまで、then()による結果だけが最も速く処理が完了したPromiseになるというわけです。
エラーハンドリングについて
Promiseは簡単な記述でエラーハンドリングも実装可能なので合わせてご紹介しておきます!
これまで、Promiseの引数には結果を格納する「resolve」だけを使ってましたが、もう1つエラー情報を格納する「reject」を利用できます。
例えば、わざとエラー情報をPromiseに与えてみましょう!
function myPromise() { return new Promise(function (resolve, reject) { reject(new Error('エラーが発生しました!')); }) }
この例では、Promise内の関数の第2引数に「reject」を設定しています。そして、このrejectにエラー情報を設定していますね。
この状態で、これまで通りPromise処理を呼び出してみましょう!
myPromise() .then(function(data) { console.log(data); }, function(error) { console.log(error.message); })
実行結果
エラーが発生しました!
この例のように、実はthen()の第2引数にはエラーハンドリングのために「reject」の値を取得することができます。このように記述しておけば、何らかのエラーが起きた場合に別の処理を実行することができるわけです。
まとめ
今回は、Node.jsで非同期処理を効率良く記述できるPromiseについて学習しました!最後に、もう一度ポイントをおさらいしておきましょう!
- 関数内でPromiseをreturnしたものをthen()により結果を取得することができる
- 複数のPromise処理を並列に実行するにはall() / race()が利用できる
- エラーハンドリングにはPromise内のrejectに格納されている値が利用できる
上記内容を踏まえて、ぜひ自分でもプログラミングに取り入れて活用できるように頑張りましょう!