JavaScript

Promise.allのcatchのタイミング

Promise.allのcatchのタイミングの扱いでつまった話を紹介します

Promiseとは

  • 非同期処理を記述できる
  • 並行処理、直列処理を記述できる

参考:JavaScript Promiseの本

エラーの扱い

並行処理の失敗をハンドリングしたい場合、各並行処理内で失敗した時にrejectメソッドを呼ぶようにします。

sample1.js
//並行処理1(失敗)
var promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("promise1");
        reject();
    }, 2000)
});
//並行処理2
var promise2 = new Promise(function (resolve, reject) {
    console.log("promise2");
    resolve();
});

Promise.all([promise1, promise2]).then(function () {
    console.log("done");
}).catch(function () {
    console.log("error");
});

上記を実行すると、errorが出力されます。

promise2
promise1
error

並行処理のうち、一つでも失敗(reject)すると、Promise.allのcatchブロック内が実行されます。

catch内実行されても他の並行処理が実行されてしまう

上のサンプルでは二つの並行処理のうち、一つで失敗するパターンですが次は両方失敗するパターンを実行してみます。

sample2.js
//並行処理1(失敗)
var promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("promise1");
        reject();
    }, 2000)
});
//並行処理2(失敗)
var promise2 = new Promise(function (resolve, reject) {
    console.log("promise2");
    reject();
});

Promise.all([promise1, promise2]).then(function () {
    console.log("done");
}).catch(function () {
    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を呼んだ上でエラーハンドリングするようにします。

sample3.js
//並行処理1(失敗)
var promise1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        console.log("promise1");
        resolve(new Error());
    }, 2000)
});
//並行処理2(失敗)
var promise2 = new Promise(function (resolve, reject) {
    console.log("promise2");
    resolve(new Error());
});

Promise.all([promise1, promise2]).then(function (error) {
    console.log("done");
    if(error){
        console.log("promise error");
    }
}).catch(function () {
    console.log("error");
});

すると、並行処理の実行が完了したあとにエラー処理を実行することができます。

promise2
promise1
done
promise error