JavaScriptの非同期処理がよくわからない、PromiseってなんぞってなったのでPromiseについてまとめてみた。
1. Promiseは何のため?
A. Promiseは非同期処理のため
これが簡潔な答えになる。
JavaScriptでは、あることをしながら別のことをするということができない。つまり、ウェブサイトのデータをダウンロードしながら画面にスピナーを表示するといったことができない。
これを解決するのが非同期処理になる。JavaScriptでは、Callbackやイベント駆動という方法がある。その新しい方法がPromiseになる。
2. Promiseのコードはどう使うか?
JavaScriptでは.then()という形で呼び出すことになる。
//Fetch APIを使う場合
fetch(url) //urlにアクセスして
.then(response => response.json()) //responseを解釈し
.then(data => console.log(data)); //データを表示する
(https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetchから引用)このケースではFetch APIを取得し、対象のURLから情報を受け取り、JSONとして解釈して、そのデータを表示するということを行っている。
言い換えれば、.then()内に与えられる関数(ここではアロー関数)を呼び出して、fetch()の戻り値→response.json()の戻り値を順に与えて処理を行っている。
3. Promiseはどのような仕組みで動くか?
Promiseは、ある関数に一時的にPromiseというものを返し、処理を続行する。このPromiseが満たされるか、失敗するか、解決することでその関数は、処理を解決する。
Promiseを返す関数には2つのコールバック関数を登録することができる。1番目のコールバック関数が呼び出されたときにPromiseは満たされ、2番目のコールバック関数が呼び出されたときにPromiseは失敗する。
つまり、以下のようなコードで2つの関数を登録する。
// funcAは処理を実行、funcBはエラーを処理
fetch(url).then(funcA, funcB)
//または
fetch(url)
.then(funcA)
.catch(funcB)
そして、解決するということは、いわばPromiseを返す関数がPromiseを受け取り、処理がペンディングされている状態になる。そして、あるPromiseがPromise以外の値で満たされたとき、連鎖してPromiseが満たされ、あるPromiseが失敗すれば、同じ理由で連鎖してPromiseが失敗する。
つまり、コードで表すと以下のようになる。
//HTTPリクエストを行い、最初にPromiseを返し、
//リクエストが返ってきたら、Promiseはresponseオブジェクトで満たされる
fetch(url)
//HTTPリクエストのPromiseが満たされるまで
//fetch()から返ってきたPromiseで解決される
//HTTPリクエストのPromiseが満たされると
//response.jsonが返すPromiseは満たされる
.then(response => response.json())
//response.json()の受け取ったPromiseが満たされるまで
//最初のthen()が返すPromiseで解決される
.then(data => displayData(data));
4. Promiseの動きを見るためのコード
Promiseは、組み込みのPromiseを返す関数以外にPromiseコンストラクタを使うことでPromiseを返す関数を作成することができる。
これを利用してミリ秒を引数として受け取り、その値を返す関数を書くと次のようになる。
//durationを引数として受け取り
//durationミリ秒後にPromiseをduratoinで満たす
function wait(duration){
return new Promise((resolve, reject)=>{
setTimeout(() => {resolve(duration)}, duration);
})
}
//start→4000→setTimeoutの順でコンソールに表示される
console.log("Start");
setTimeout(() => {console.log("setTimeout")}, 5000);
wait(4000).then(time => {console.log(time)});
//start→setTimeout→4000の順でコンソールに表示される
console.log("Start");
setTimeout(() => {console.log("setTimeout")}, 3000);
wait(4000).then(time => {console.log(time)});
5.まとめ
Promiseは仮の値を関数に与えることで処理したことにして(処理したとみなし)、次の処理に進む。そのうえで、最初のPromiseが満たされたとき、連鎖的にPromiseが満たされて結果が返ってくる。