2022年2月17日 非同期処理:コールバック/Promise/Async - Promiseチェーンで値を返す (JavaScript Primer)
Promiseチェーンで値を返す
thenメソッドなどのコールバック関数は次のチェーンに値を渡すこともできます。 以下はreturnして次のthenメソッドの引数に値を渡している例です。
Promise.resolve(1).then((value) => { console.log(value); // => 1 return value * 2; }).then(value => { console.log(value); // => 2 return value * 2; }).then(value => { console.log(value); // => 4 }).then(value => { console.log(value); // => undefined });
3つ目のthenで値をreturnしていないので4つ目のthenの引数であるvalueはundefinedとなります。
コールバック関数でPromiseインスタンスを返す
Promise.rejectでPromiseState内部プロパティをrejected
の状態でインスタンス化したものをreturn
しているので、thenは無視されてcatchに処理が移ります。
これまではfulfilled
を返すと学びましたが、このような手法でrejected
を返すことで、その後のPromiseチェーンでthenではなくcatchの処理をさせる事ができます。
Promise.resolve().then(function onFulfilledA() { return Promise.reject(new Error("失敗")); }).then(function onFulfilledB() { console.log("onFulfilledBは呼び出されません"); }).catch(function onRejected(error) { console.log(error.message); // => "失敗" }).then(function onFulfilledC() { console.log("onFulfilledCは呼び出されます"); });
以下のように、catchの処理の中でrejectをreturnさせて、エラー処理を次のチェーンへと連続させることも出来ます。
function main() { return Promise.reject(new Error("エラー")); } // mainはRejectedなPromiseを返す main().catch(error => { // mainで発生したエラーのログを出力する console.log(error); // Promiseチェーンはそのままエラーを継続させる return Promise.reject(error); }).then(() => { // 前のcatchでRejectedなPromiseが返されたため、この行は実行されません }).catch(error => { console.log("メインの処理が失敗した"); });
[ES2018]Promiseチェーンの最後に処理を書く
Promiseのfinallyメソッドは成功時、失敗時どちらの場合でも呼び出されるコールバック関数を登録できます。 try...catch...finally構文のfinally節と同様の役割を持つメソッドです。
以下はresolve()とreject()のどちらかをランダムに発生させ、thenかcatchを行った後にfinallyの処理を呼び出しています。
const promise = Math.random() < 0.5 ? Promise.resolve() : Promise.reject(); promise.then(() => { console.log("Promise#then"); }).catch((error) => { console.log("Promise#catch"); }).finally(() => { // 成功、失敗どちらの場合でも呼び出される console.log("Promise#finally"); });
次のコードでは、リソースを取得してthenで成功時の処理、catchで失敗時の処理を登録しています。 また、リソースを取得中かどうかを判定するためのフラグをisLoadingという変数で管理しています。 成功失敗どちらにもかかわらず、取得が終わったらisLoadingはfalseにします。 thenとcatchの両方でisLoadingへfalseを代入できますが、finallyメソッドを使うことで代入を一箇所にまとめられます。
let isLoading = true;
のフラグを処理が終わったらfalse
にしたいわけですが、finallyメソッドを使わないとthenとcatchそれぞれに書かなくてはいけないところを、必ず最後に実行されるfanallyメソッドを使う事で一箇所にまとめられています。
function dummyFetch(path) { return new Promise((resolve, reject) => { setTimeout(() => { if (path.startsWith("/resource")) { resolve({ body: `Response body of ${path}` }); } else { reject(new Error("NOT FOUND")); } }, 1000 * Math.random()); }); } // リソースを取得中かどうかのフラグ let isLoading = true; dummyFetch("/resource/A").then(response => { console.log(response); }).catch(error => { console.error(error); }).finally(() => { isLoading = false; console.log("Promise#finally"); });
まとめ
- Promiseチェーンではコールバックで返した値を次のコールバックの引数として渡せます
- catch内でPromise.rejectメソッド使ってPromiseStateを
rejected
で返せばその後のPromiseチェーンのthenを無視させcatchの処理を行えます - finallyメソッドはPromiseState内部プロパティが
fulfilled
rejected
どちらでも処理を実行します