Promiseチェーンを途中でスキップしたい

Expressアプリのコードを書いてるとき、Promise周りでうまくいかないことがあった。

例えば以下のようなコードがある。

```js
app.get('/users/:id', (req, res) => {
  return User.findById(req.params.id)
    .then((user) => {
      if (!user) {
        return res.status(404).json({message: 'Not found'});
      }
      return user;
    })
    .then((user) => {
      return doSomething(user);
    })
    .then(() => {
      return res.status(200).json({message: 'Successfully processed'})
    })
    .catch((err) => {
      if (err.name === 'CastError') {
        return res.status(404).json({message: 'Not found'});
      }
      return res.status(500).json({message: 'Server error'});
    });
});
```

ユーザIDでユーザを検索し、ユーザが存在すればそのまま処理を継続、ユーザが存在しなければ `404 Not found` を返すという処理。

`User`モデルはmongooseを使っている想定で、mongooseはIDが存在しなければthenにnullを渡し、存在すればuserオブジェクトを渡す。
また、IDがMongoDBのObjectIDの形式になっていない場合はrejectedとなり、`CastError`を渡す。

ここで問題なのは、IDが存在しなかった場合、５行目の`res.json()`で処理を終了したいのだが、Promiseチェーンはここで終了せず次のthenに進んでしまうこと。

Promiseチェーンを途中で終了させるにはどうしたらよいのか？

## throwする
Promiseチェーンは`try-catch`のように動作するので、Promiseチェーンを終了させたい場所でthrowすればcatchに処理が移る。catchではエラーの種類によってレスポンスを変えるようにすれば良い。

```js
app.get('/users/:id', (req, res) => {
  return User.findById(req.params.id)
    .then((user) => {
      if (!user) {
        throw new NotFound();
      }
      return user;
    })
    .then((user) => {
      return doSomething(user);
    })
    .then(() => {
      return res.status(200).json({message: 'Successfully processed'})
    })
    .catch((err) => {
      if (err instanceof NotFound) {
        return res.status(404).json(err);
      } else if (err.name === 'CastError') {
        return res.status(404).json({message: 'Not found'});
      }
      return res.status(500).json({message: 'Server error'});
    });
});

class NotFound extends Error {
  constructor(message) {
    super(message);
    this.description = message;
    this.name = 'NotFound';
    this.status = 404;
  }
}
```

ここではカスタムエラークラスを作っていて、`instanceof`でエラークラスを判定して処理をわけるという方法をとっている。

## bluebirdを使う
Native PromiseにはないbluebirdのFiltered Catchを使うと、Javaなどの他のプログラミング言語で見るような書きかたができる。

```js
...
const mongoose = require('mongoose');
const bluebird = require('bluebird');
mongoose.Promise = bluebird;  // Promiseにbluebirdを使う
...

app.get('/users/:id', (req, res) => {
  return User.findById(req.params.id)
    .then((user) => {
      if (!user) {
        throw new NotFound();
      }
      return user;
    })
    .then((user) => {
      return doSomething(user);
    })
    .then(() => {
      return res.status(200).json({message: 'Successfully processed'})
    })
    .catch(NotFound, (err) => {
      return res.status(404).json(err);
    })
    .catch({name: 'CastError'}, (err) => {
      return res.status(404).json({message: 'Not found'});
    })
    .catch((err) => {
      return res.status(500).json({message: 'Server error'});
    });
});
```

Filtered Catchは`catch(ErrorClass, handler)`という書き方ができて、catchしたエラーが`ErrorClass`のインスタンスだったら、handlerを実行する。そして、一つのcatchが実行されたら、その後のcatchは実行されない。

上のコードでいえば、`NotFound`にマッチしたら、その後の２つのcatchは実行されない。

なお、`catch({name: 'CastError'}, handler)`は、catchに渡されたエラーオブジェクトerrのnameプロパティが`CastError`だったら、そのhandlerを実行するという意味。

## 参考
* http://stackoverflow.com/questions/26076511/handling-multiple-catches-in-promise-chain
* http://bluebirdjs.com/docs/api/catch.html#filtered-catch