Promise.allのcatchのタイミングの扱いでつまった話を紹介します
Promiseとは
- 非同期処理を記述できる
- 並行処理、直列処理を記述できる
エラーの扱い
並行処理の失敗をハンドリングしたい場合、各並行処理内で失敗した時にrejectメソッドを呼ぶようにします。
//並行処理1(失敗)
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
reject();
}, 2000)
});
//並行処理2
const promise2 = new Promise((resolve, reject) => {
console.log("promise2");
resolve();
});
Promise.all([promise1, promise2]).then(() => {
console.log("done");
}).catch(() => {
console.log("error");
});
上記を実行すると、errorが出力されます。
promise2
promise1
error
並行処理のうち、一つでも失敗(reject)すると、Promise.allのcatchブロック内が実行されます。
catch内実行されても他の並行処理が実行されてしまう
上のサンプルでは二つの並行処理のうち、一つで失敗するパターンですが次は両方失敗するパターンを実行してみます。
//並行処理1(失敗)
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
reject();
}, 2000)
});
//並行処理2(失敗)
const promise2 = new Promise((resolve, reject) => {
console.log("promise2");
reject();
});
Promise.all([promise1, promise2]).then(() => {
console.log("done");
}).catch(() => {
console.log("error");
});
すると以下のようになります。
promise2
error
promise1
並行処理1はsetTimeoutで2秒後にコールバック内の処理が実行されるので、先に並行処理2が実行されます。並行処理2はconsole.log(promise2)が呼ばれた直後にrejectメソッドを呼んでいるので、失敗となります。よって、すぐにPromise.allのcatchブロックが実行されてしまいます。しかし、並行処理1は未だ実行中なので、errorが出力された後にpromise1が出力されます。このように、並行処理のうち一つでも失敗したらcatchブロックが実行されますが、実行中の他の並行処理を待たずに実行されてしまいます。
resolveでハンドリングする
rejectを呼んでしまうと、待たずに実行されてしまうので、resolveを呼んだ上でエラーハンドリングするようにします。
//並行処理1(失敗)
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("promise1");
resolve(new Error());
}, 2000)
});
//並行処理2(失敗)
const promise2 = new Promise((resolve, reject) => {
console.log("promise2");
resolve(new Error());
});
Promise.all([promise1, promise2]).then((results) => {
console.log("done");
const includeError = results.some((result) => result instanceof Error);
if(includeError){
console.log("promise error");
}
}).catch(() => {
console.log("error");
});
すると、並行処理の実行が完了したあとにエラー処理を実行することができます。
promise2
promise1
done
promise error