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と同じ動作になる