Promise 非同期処理入門(JavaScript)
目次
はじめに
promiseは、単一の非同期操作の最終的な結果を表すオブジェクトとその仕組みです。また、並列、並行処理におけるプログラミング言語のデザインの一種です。非同期処理といえばコールバックがありますが、ここではPromiseに重点をおいて記述していきたいと思います。
Promiseの記法
Promiseは統一的なインターフェースで記述できるようにルールを仕様化しています。
Constructor
promiseをnewでインスタンスする
- promiseオブジェクトで処理が成功した場合にはresolveを、失敗した場合にはrejectを使用します。
- 成功した場合の処理はPromise.thenに繋がり、失敗した場合はPromise.catchに繋がります。(コールバック関数)
- promiseオブジェクトの状態がresolve(Fulfilled)、reject(Rejected)の状態になればそれ以降変化することはありません。
- rejectを指定しなくても、resolveにより.catch以降のコールバックは機能します。
関数に組み込むPromise
promiseオブジェクトをreturnで返す
- promiseオブジェクトをreturnで返します
- resolve(100)から.thenに記述されたコールバック関数に値が渡されます。
- rejectを指定しなくても、resolveにより.catch以降のコールバックは機能します。
Promiseのメソッドチェーン
これまでのsample codeでthenとcatchをメソッドチェーンで繋いで表記してきました。実はメソッドチェーンによって処理の挙動に流れを作ることができるからです。
ではsample codeで確認しましょう。
上記のコードは3つの関数をpromiseオブジェクトでメソッドチェーンにしたものです。一つ目の関数はvalueを2倍にして返す関数、二つ目はvalueに1をプラスする関数、三つ目はvalueを返す関数です。これら三つの関数をpromiseオブジェクトで扱います。
(解説)
- メソッドチェーンで繋いだpromise chainの順番通りに処理されます。
- promiseオブジェクトをreturnする事ができます。
(処理の流れ)
- Promise.resolve(2)から2の値がincrementに渡されます。
- incrementの値は3になり、その値がpromiseオブジェクトとなり、そのままdoubledNumberに渡されます。
- doubledNumberで値は6となり、そのままputOut関数に渡されます。
- 最後にputOut関数が値(6)を出力します。
処理のflow図
処理の途中でonRejectedの状態になる時は次のような場合になります。
- 例外が発生した時
- Rejectedな状態になるpromiseオブジェクトがreturnされた時
例外が発生したケースの処理の挙動
- incrementで例外が発生した場合、処理はonRejectedからputOutへとなります。
- doubledNumberで例外が発生した場合、処理はonRejectedからputOutへとなります。
(注意) ここではonRejectedとputOutにはcatchのpromise chainが記述されていません。
Promise.all
Promise.allはpromiseオブジェクトの配列を扱います。promise配列の全ての通信がFulfilled,Rejectedされた後にthenが呼ばれます。
sample codeで確認しましょう。
(解説)
- 各promiseの値が配列になっています。
- promise1の右辺はpromiseオブジェクトでnew Promiseのショートカットです。
- promise4の右辺のfetchはJavaScriptのネイティブメソッドでPromiseを返します。
- 配列内のpromiseの実行は全て同時に実行されます。(並列、並行処理)
本当にPromise.allが並行処理しているか
Promise.allの並行処理の性質を確認するにはsetTimeout関数を使用します。
(検証)
setTimeout関数は何秒後かに処理を実行する関数であるから、promise.allが逐次的であれば処理が終わるまでに10秒かかるはずですね。ですがPromise.allは同時に処理を実行する性質を持っているため、4秒ほどで処理が完了するはずです。
実際結果はどうでしょう。
4001ms
(4) [1000, 2000, 3000, 4000]
(解説)
- timerPromise関数はms秒後にresolveする関数です。
- 4秒ほどで処理は完了することから逐次的ではなく同時に実行している事がわかります。
Promise.race
Promise.allと同じ様にpromiseオブジェクトの配列を引数に渡します。Promise.raceはどれか一つでもpromiseがFulfilledやRejectedになったら次の処理を実行します。
(解説)
- timerPromise関数はms秒後にresolveする関数です。
- timerPromise(1000)が実行されfulfilled状態になった事でthen以降の処理が実行されました。
- Promise.raceは、 一番最初のpromiseオブジェクトがFulfilledとなっても、他のpromiseがキャンセルされるわけではありません。
Promise.finally
Promise.finallyはresolve, rejectのどちらの場合でも呼び出せるメソッドです。
ではsample codeを見てみましょう。
(解説)
- createPost関数はposts配列にオブジェクトを加える関数です。
- createPost関数はpromise.finallyの処理を実行します。
- getPosts関数はposts配列をforEachし、コンソールに出力する関数です。
- グローバル変数のisLoading= false以外はtrueで出力されています。
Promise.finallyによってisLoadingの値をひとまとめにする事ができます。
まとめ
非同期処理のPromiseの主要な機能をまとめました。コールバックでの非同期処理と比べてルールを統一しやすいインターフェースでPromiseを記述する事ができるのですっきりとしたコードにできるのではないでしょうか。promiseにはまだまだいろんな機能に加えてasync/awaitとの連携で使われる事が多いです。いろんな非同期処理のパターンを形成してみましょう。
参考 promiseの本