今日までコールバックと戦ってきたみんなを、Promiseを信じた魔法少女を、私は泣かせたくない。

もう5年も前の記事になりますが、以下の記事がとても楽しげに`Promise`の厄介な側面を洗い出していて感動したので、アンサー記事的なものを書いてみたくなりました。

[コールバック……駆逐してやる…この世から…一匹…残らず!!](https://qiita.com/KDKTN/items/4c6986049d204f0645d8)

上記の参考記事では`Promise`系の実装として`jQuery.Deferred`を取り扱っていますが、本記事では`V8`の組込オブジェクトとしての`Promise`を取り扱います。

(※なぜか『魔法少女まどかマギカ』の一部ネタバレが含まれるので注意してください)

## 目に焼き付けておきなさい。Promiseを使い間違えるって、そういうことよ。
参考記事からの引用。コールバック地獄の原型です。

```js
$.get("hoge.txt", function(hoge) {
  $.get("piyo.txt", function(piyo) {
    $.get("nyan.txt", function(nyan) {
      $.get("myon.txt", function(myon) {
        console.log(hoge + piyo + nyan + nyan);
      });
    });
  });
});
```

参考記事からの引用。`Promise`という魔法が生まれてすぐの段階です。（`then`でチェーンしているのでそこまで酷くない）

```js
let hoge;
$.get("hoge.txt")
  .then(function(_hoge) {
    hoge = _hoge;
    return $.get("piyo.txt");
  })
  .then(function(piyo) {
    console.log(hoge + piyo);
  });
```

参考記事からの引用。`Promise`が暴走し、魔女化した状態です。（変数`hoge`をなくした代わりにチェーンが崩壊している）

```js
$.get("hoge.txt")
  .then(function(hoge) {
    return $.get("piyo.txt").then(function(piyo) {
      console.log(hoge + piyo);
    });;
  });
```

## asyncと契約して、魔法少女になってよ！
`Promise`に息苦しさを覚えて、最初に手が伸びる先と言えばやはり`async`です。
参考記事からの引用。（順次実行での比較なので`async.parallel`は`async.series`に書き換えています）

```js
function get(path) {
  return function(callback) {
    $.get(path).then(function(data) { callback(null, data); });
  };
}
async.series({
  hoge: get("hoge.txt"),
  piyo: get("piyo.txt")
},
function(err, results) {
    console.log(results.hoge + results.piyo);
});
```

一見キレイに書けたように見えますが、悲しいかな`async.series`は「hogeの結果をpiyoの処理で扱う」ということに不向きで、そういうときは`async.waterfall`を使うことになります。

```js
function get(path) {
  return function(callback) {
    $.get(path).then(function(hoge) { callback(null, hoge); });
  };
}
function get2(path) {
  return function(hoge, callback) {
    $.get(path).then(function(piyo) { callback(null, hoge, piyo + ' with ' + hoge); });
  };
}
async.waterfall([
  get('hoge.txt'),
  get2('piyo.txt')
], function(err, hoge, piyo) {
  console.log({ hoge, piyo });
});
```
これで「hogeの結果をpiyoの処理で扱う」ことは出来ましたが、悲しいかな`async.waterfall`に指定する関数は「前の関数の返却値を引数で受ける」ように実装する必要があります。
つまり「コールバックを引数に受ける、1つ目の関数」「コールバックと1つ目の返却値を引数に受ける、2つ目の関数」という風に1つずつ用意していくのです。
そうすると「2番目に関数を加えたい…」「引数が8個を超えてきた…」などの悲劇がよく生まれます。

## Promiseが使いづらいなんて言われたら、私、そんなのは違うって。何度でもそう言い返せます。
`Promise`を扱うコツは2つあります。
1. やりたいことを順番に（ネストすることなく）`then`に入れてあげること。
2. メソッドチェーンで値を引きずり回さないこと。つまり**値をメソッドチェーンの外側に持つ**こと。

それを念頭に入れて実直に行くと、こんな形になります。

```js
const results = {};
Promise.resolve()
  .then(() => $.get('hoge.txt')).then(hoge => results.hoge = hoge)
  .then(() => $.get('piyo.txt')).then(piyo => results.piyo = piyo)
  .then(() => console.log(results.hoge + results.piyo));
```
最初の`Promise.resolve()`はメソッドチェーン生成処理です。全ての関数を`then`に入れるためのおまじないとでも思ってください。

一応、参考記事では型付けにこだわっていましたので、`then`が受ける`Function`の戻りを`{Promise|any}`から`{Promise}`に寄せたものも記載しておきます。一応。

```js
const results = {};
Promise.resolve()
  .then(() => $.get('hoge.txt')).then(hoge => Promise.resolve(results.hoge = hoge))
  .then(() => $.get('piyo.txt')).then(piyo => Promise.resolve(results.piyo = piyo))
  .then(() => Promise.resolve(console.log(results.hoge + results.piyo)));
```

重複している代入処理(`results.hoge = hoge`と`results.piyo = piyo`)は一工夫できますが、可読性は上がりません。

```js
const results = {};
function acceptAs(name) {
  return o => results[name] = o;
}
Promise.resolve()
  .then(() => $.get('hoge.txt')).then(acceptAs('hoge'))
  .then(() => $.get('piyo.txt')).then(acceptAs('piyo'))
  .then(() => console.log(results.hoge + results.piyo));
```

いっそのこと`then`から完全に無名関数を排除した方がシンプルです。こうすると`Promise`が関数を呼び出していくメソッドチェーンにすぎないことがよく分かります。

```js
const results = {};
function getHoge() {
  return $.get('hoge.txt').then(hoge => results.hoge = hoge);
}
function getPiyo() {
  return $.get('piyo.txt').then(piyo => results.piyo = piyo);
}
function out() {
  console.log(results.hoge + results.piyo);
}
Promise.resolve().then(getHoge).then(getPiyo).then(out);
```

スコープを気にする人ならカプセル化もしたいですよね。これは`Promise`実装の1つの完成形だと思います。

```js
const Capsule = {
  getHoge: function() {
    return $.get('hoge.txt').then(hoge => Capsule.hoge = hoge);
  },
  getPiyo: function() {
    return $.get('piyo.txt').then(piyo => Capsule.piyo = piyo);
  },
  out: function() {
    console.log(Capsule.hoge + Capsule.piyo);
  }
};
Promise.resolve().then(Capsule.getHoge).then(Capsule.getPiyo).then(Capsule.out);
```
この**オブジェクト指向との相性の良さ**は、`Promise`を理解する上での勘所だと思います。なぜなら`Promise`には「関数を呼び出すが、状態は持たない」という特性があるからです。だから「状態を持った関数群」であるオブジェクトとは相性が良いのです。

## 訳が分からないよ。どうして人間はそんなに、同期的なコーディングスタイルにこだわるんだい？
参考記事で紹介されているように、`co`と`generator`でもかっこよく書けます。

```js
const co = require('co');
co(function*() {
  const hoge = yield $.get('hoge.txt');
  const piyo = yield $.get('piyo.txt');
  console.log(hoge + piyo);
});
```
ここで行われていることを簡単に説明すると`co`が`generator`を`next`で進めて`yield`で一旦`generator`を抜けて返却された`Promise`がresolveされたら`co`がその値をさらに`next`に渡して`yield`を上書きすることで次の`yield`へ進んで…

もはやバイオリン奏者の怪我を治してあげたかったのか、彼と恋仲になりたかったのか、本当の気持ちが分からなくなりそうです。JavaScript未経験の魔法少女に`co`を使ったソースコードを見せたら、ソウルジェムは一瞬でどす黒く濁るでしょう。

## ほむらちゃん、ごめんね。私、Promiseの魔法少女になる。私、やっとわかったの。
最初の方に戻ってみましょう。

```js
const results = {};
Promise.resolve()
  .then(() => $.get('hoge.txt')).then(hoge => results.hoge = hoge)
  .then(() => $.get('piyo.txt')).then(piyo => results.piyo = piyo)
  .then(() => console.log(results.hoge + results.piyo));
```
これの何がダメだったかというと…あ、あれ？なんだか…そこまで難しくない…？

そうだよね、`then`の中に処理を書いただけだもんね…。

ちなみに`then`の中の関数は`Promise`を返す必要はありません。

```js
const results = {};
Promise.resolve()
  .then(() => 'a' + 'b').then(hoge => results.hoge = hoge)
  .then(() => 'c' + 'd').then(piyo => results.piyo = piyo)
  .then(() => console.log(results.hoge + results.piyo)); // ⇒'abcd'
```
こういうものも大丈夫です。むしろ、こういうものに慣れた方がいい。魔法少女が銃火器で闘うくらい自然なことです。

## あるよ。順次実行も、並列実行も、あるんだよ。
hoge.txtとpiyo.txtを順次実行ではなく並列実行で読みたい場合は、順次実行用のコードを`Promise.all([])`に入れます。

```js
const results = {};
Promise.all([
  Promise.resolve().then(() => $.get('hoge.txt')).then(hoge => results.hoge = hoge),
  Promise.resolve().then(() => $.get('piyo.txt')).then(piyo => results.piyo = piyo)
])
.then(() => console.log(results.hoge + results.piyo));
```

常に`Promise.resolve()`で始めて`Promise.all([])`も`then`に入れるルールにすると、より強固に統一されたコーディングスタイルとなりますが、そこまでしなくていいでしょう。

```js
const results = {};
Promise.resolve()
  .then(() => Promise.all([
    Promise.resolve().then(() => $.get('hoge.txt')).then(hoge => results.hoge = hoge),
    Promise.resolve().then(() => $.get('piyo.txt')).then(piyo => results.piyo = piyo)
  ]))
  .then(() => console.log(results.hoge + results.piyo));
```

オブジェクト指向な感じに寄せると、メソッドチェーン部分はこんな風になります。

```js
Promise.all([
  Promise.resolve().then(Capsule.getHoge),
  Promise.resolve().then(Capsule.getPiyo)
]).then(Capsule.out);
```

`Promise`の真価は、順次実行と並列実行の同居のしやすさで発揮されます。（`async`や`generator`が順次実行と並列実行の同居を得意としていないだけのような気もします）

```js
// hogeとpiyoを並列で読み取って、読み終わったらnyanも読み取って、それも読み終わったら出力する
Promise.all([
  Promise.resolve(Capsule.getHoge),
  Promise.resolve(Capsule.getPiyo)
])
.then(Capsule.getNyan)
.then(Capsule.out);
```
このように`Promise`は順次実行と並列実行を縦横無尽に組み合わせることができ、実行順序についても一目で理解できます。

## これがコールバック。JavaScriptに選ばれた女の子が、契約によって生み出す宝石よ。
コールバックはJavaScriptの欠点のように語られることもありますが、そんなことはないと思います。「コールバックが無いように見せかけたい」という願いが、形のない悪意となって、人間を内側から蝕んでゆくの、と誰かが言ってました。

`ES6`や`V8`が登場し、最初の魔法`Promise`が、巡り巡ってコールバック地獄の歴代解決策に取って代わるとしたら…どこかで聞いたことのある話ですよね。（伏線回収できた？）

Promiseちゃん、ありがとう。あなたは私の、最高の友達だったんだね。

おしまい

---
リクエストにお答えして、続きを書きました。
[そんな…どうして…？さやかちゃん、async/awaitでPromiseから人を守りたいって、そう思って魔法少女になったんだよ。](https://qiita.com/almiraj/items/2da97f6cc36143f29f8a)