Promise の reject 処理を連鎖させる

[Promise](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise) が reject （失敗）になったときの処理（`catch()` や `then()` の第二引数に指定した処理）を連鎖させる方法。

# 環境
**ブラウザ**
`Chrome 72.0.3626.119 (64bit)`

**OS**
`Windows 10 64bit`

**jQuery**
`3.3.1`

# ダメパターン
```js
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);
    });
```

```text:実行結果
Execute!!
catch(1) : ERROR
then(1) : CATCH(1)
```

- １つ目の `catch()` の後に呼ばれているのは `then()` の処理になっている
- ２つ目の `catch()` は呼ばれていない（コレを呼ぶようにしたい）

## catch() した後は状態が fullfilled の Promise が返る
> catch()  メソッドで返される Promise は、onRejected がエラーを投げた場合、または返
> される Promise それ自体が reject の場合は、reject となります。
> **それ以外の場合は、resolve となります。**

[Promise.prototype.catch() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch#%E5%BC%95%E6%95%B0)

- `catch()` 自体は `Promise` オブジェクトを返すが、その `Promise` の状態は `catch()` に渡した関数(`onRejected`)の処理によって変化する。
- 状態が **rejected** になる
    - `onRejected` が例外をスローした
    - もしくは、 rejected 状態の `Promise` を返した
- 状態が **fullfilled** になる
    - 上記以外

※MDN のページでは「resolve となります」と書いているが、 [States and Fates | GitHub](https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md) とか [ECMAScript の仕様書](https://www.ecma-international.org/ecma-262/6.0/#sec-promise-objects) を読む感じ、 resolved は rejected 状態も含む言葉に読めたので、ここでは表現を fullfilled に変えている。

## catch() をチェーンさせる
```js
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);
    });
```

```text:実行結果
Execute!!
catch(1) : ERROR
catch(2) : CATCH(1)
catch(3) : CATCH(2)
```

- この例では、次の２つの方法で `catch()` が返す `Promise` を rejected 状態にしている
    - `Promise.reject()` で生成した rejected 状態の `Promise` を返す
    - `throw` で例外を投げる
- これにより、 `catch()` が返した `Promise` は rejected になり、続く `catch()` に処理が連鎖するようになる

# jQuery の fail() の動き
ところで、 jQuery の Promise が持つ `fail()` メソッドは次のような動きをする。

```js
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);
    });
```

```text:実行結果
Execute!!
fail(1) : ERROR
fail(2) : ERROR
```

- `fail()` は普通に連鎖する
- 引数も、最初の `reject()` で渡した値が連続で渡されている

## ECMAScript の Promise 互換
v3.x からは、 jQuery の `Promise` も ECMAScript の `Promise` と互換性を持つようになっているので、 `catch()` が使用できる。

```js
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);
    });
```

```text:実行結果
Execute!!
catch(1) : ERROR
catch(2) : CATCH(1)
done(1) : CATCH(2)
```

- 当然、 ECMAScript の `Promise` と同じ動作になる