Promiseを並列実行したいときPromise.all使うことあると思いますが、Promise.allでは全てのErrorをcatchできません。
今回はPromise.allの挙動とそこを解消する方法についてまとめていきます。
目次
Promise.allの挙動について
Promise.allはPromiseのリストを並列実行できます。また、Promise.all自身もPromiseを返却します。
全てのPromiseがresolveであればPromise.allもresolveされ、1つでもrejectがあるとPromise.allはその時点でrejectされるという処理の流れです。
全てresolveの場合のPromise.allの挙動
まずは全てresolveの場合のサンプルコード。この例では[1,2,3]が表示されます。
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
resolve(2);
});
const p3 = new Promise((resolve, reject) => {
resolve(3);
});
Promise.all([p1, p2, p3])
.then((values) => {
console.log(values);
})
.catch((e) => {
console.log(e);
});
rejectした場合のPromise.allの挙動
次にp2とp3がrejectした場合。
この場合はPromise.allのcatchブロックに入ります。しかし、p2のrejectでcatchに入り処理をして終了するため、p3のエラーは無視されてしまいます。
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
reject("error2");
});
const p3 = new Promise((resolve, reject) => {
reject("error3");
});
Promise.all([p1, p2, p3])
.then((values) => {
console.log(values);
})
.catch((e) => {
console.log(e);
});
どうやって全てのErrorをcatchするか?
Promise.allではすべてをcatchすることはできません。
Promise.allをラッピングすることでcatchはできないけどErrorを捉えられるようにします。
このようにラッピングします。
エラーが起きた場合でもresolveにしてしまい、resolveの引数には正常値、エラーをまとめたリストを入れるようにします。
2つめの値がnullじゃなければエラー発生ととらえることができます。
const execParallelPromise = (promiseList: Promise<any>[]): Promise<any> => {
return Promise.all(
promiseList.map((p) => {
return new Promise((resolve, reject) => {
p.then((value) => {
resolve([value, null]);
})
.catch((err) => {
resolve([null, err]);
});
});
})
);
};
先ほどの複数がrejectされる例で使ってみましょう。
[[1, null], [null, “error2”], [null, “error3”]]という値が表示されます。
1つ目が正常値、2つ目がエラーになっていることが分かると思います。
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
reject("error2");
});
const p3 = new Promise((resolve, reject) => {
reject("error3");
});
execParallelPromise([p1, p2, p3]).then((values) => {
console.log(values);
});
欠点
このやり方だとanyで型推論が効かないため型の恩恵が消えてしまいます。execParallelPromiseは色々anyにしないと動かないので仕方ないですが…。
この場合に戻り値に型を指定するには以下のやり方になります。微妙な感じですが…。
parallelPromise([p1, p2, p3]).then((values) => {
console.log(values);
const [result, error]: [number | null, string | null] = values[0];
console.log(result, error);
});