Promise が reject (失敗)になったときの処理(catch()
や then()
の第二引数に指定した処理)を連鎖させる方法。
環境
ブラウザ
Chrome 72.0.3626.119 (64bit)
OS
Windows 10 64bit
jQuery
3.3.1
ダメパターン
function promise(message) {
return new Promise((resolve, reject) => {
console.log(message);
reject("ERROR");
});
}
promise("Execute!!")
.catch((error) => {
console.log("catch(1) : " + error);
return "CATCH(1)";
})
.then((success) => {
console.log("then(1) : " + success);
return "THEN(1)";
})
.catch((error) => {
console.log("catch(2) : " + error);
});
Execute!!
catch(1) : ERROR
then(1) : CATCH(1)
- 1つ目の
catch()
の後に呼ばれているのはthen()
の処理になっている - 2つ目の
catch()
は呼ばれていない(コレを呼ぶようにしたい)
catch() した後は状態が fullfilled の Promise が返る
catch() メソッドで返される Promise は、onRejected がエラーを投げた場合、または返
される Promise それ自体が reject の場合は、reject となります。
それ以外の場合は、resolve となります。
Promise.prototype.catch() - JavaScript | MDN
-
catch()
自体はPromise
オブジェクトを返すが、そのPromise
の状態はcatch()
に渡した関数(onRejected
)の処理によって変化する。 - 状態が rejected になる
-
onRejected
が例外をスローした - もしくは、 rejected 状態の
Promise
を返した
-
- 状態が fullfilled になる
- 上記以外
※MDN のページでは「resolve となります」と書いているが、 States and Fates | GitHub とか ECMAScript の仕様書 を読む感じ、 resolved は rejected 状態も含む言葉に読めたので、ここでは表現を fullfilled に変えている。
catch() をチェーンさせる
function promise(message) {
return new Promise((resolve, reject) => {
console.log(message);
reject("ERROR");
});
}
promise("Execute!!")
.catch((error) => {
console.log("catch(1) : " + error);
return Promise.reject("CATCH(1)");
})
.then((success) => {
console.log("then(1) : " + success);
return "THEN(1)";
})
.catch((error) => {
console.log("catch(2) : " + error);
throw "CATCH(2)";
})
.catch((error) => {
console.log("catch(3) : " + error);
});
Execute!!
catch(1) : ERROR
catch(2) : CATCH(1)
catch(3) : CATCH(2)
- この例では、次の2つの方法で
catch()
が返すPromise
を rejected 状態にしている-
Promise.reject()
で生成した rejected 状態のPromise
を返す -
throw
で例外を投げる
-
- これにより、
catch()
が返したPromise
は rejected になり、続くcatch()
に処理が連鎖するようになる
jQuery の fail() の動き
ところで、 jQuery の Promise が持つ fail()
メソッドは次のような動きをする。
function jqPromise(message) {
let deferred = new $.Deferred();
console.log(message);
deferred.reject("ERROR");
return deferred.promise();
}
jqPromise("Execute!!")
.fail((error) => {
console.log("fail(1) : " + error);
return "FAIL(1)";
})
.done((success) => {
console.log("done(1) : " + success);
})
.fail((error) => {
console.log("fail(2) : " + error);
});
Execute!!
fail(1) : ERROR
fail(2) : ERROR
-
fail()
は普通に連鎖する - 引数も、最初の
reject()
で渡した値が連続で渡されている
ECMAScript の Promise 互換
v3.x からは、 jQuery の Promise
も ECMAScript の Promise
と互換性を持つようになっているので、 catch()
が使用できる。
function jqPromise(message) {
let deferred = new $.Deferred();
console.log(message);
deferred.reject("ERROR");
return deferred.promise();
}
jqPromise("Execute!!")
.catch((error) => {
console.log("catch(1) : " + error);
throw "CATCH(1)";
})
.catch((error) => {
console.log("catch(2) : " + error);
return "CATCH(2)";
})
.done((success) => {
console.log("done(1) : " + success);
})
.catch((error) => {
console.log("catch(3) : " + error);
});
Execute!!
catch(1) : ERROR
catch(2) : CATCH(1)
done(1) : CATCH(2)
- 当然、 ECMAScript の
Promise
と同じ動作になる