LoginSignup
23
13

More than 5 years have passed since last update.

Promise の reject 処理を連鎖させる

Posted at

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
13