そんな…どうして…？さやかちゃん、async/awaitでPromiseから人を守りたいって、そう思って魔法少女になったんだよ。

`async/await`の話も入れてほしいというご要望があったので、続きを書きました。
前回記事：[今日までコールバックと戦ってきたみんなを、Promiseを信じた魔法少女を、私は泣かせたくない。](https://qiita.com/almiraj/items/859ab4b605f2fb54e5c9)

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

## 人物紹介 (適当)

* まどか
  * `Promise`で戦う魔法少女。歴代コールバック対策の全てを無かったことにしようと目論む。
* ほむほむ
  * 12年間ずっとまどかに`Promise`の使い方を教えてきた謎の魔法少女。
* マミさん
  * 目新しい技術を手にすると何も恐くなくなって死ぬ。
* さやかちゃん(笑)
  * コールバックや`Promise`が嫌いだったが、`async/await`で魔法少女になった。
* 杏子
  * クロージャの`this`について調べている途中で頭がおかしくなった父親を持つ、心優しい魔法少女。

## アンタが噂のasync/awaitってやつか。妙な技を使いやがる。
まずは`Promise`と`async/await`で順次実行を比較。

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

```js
(async() => {
  const hoge = await $.get('hoge.txt');
  const piyo = await $.get('piyo.txt');
  console.log(hoge + piyo);
})();
```

構成は似ていますが、`async/await`側は劇的にコード量が減っています。
`Promise`側は本来、変数`results`のスコープを閉じるためにさらにコード量を増やす必要がありますから、`async/await`恐るべしです。

でも、マミさんが言ってました。
危険を冒してまで叶えたい願いがあるのかどうか、じっくり考えてみるべきだと思うの、って。

## さあ、行こう。今夜もPromiseをやっつけないと。
次に`Promise`と`async/await`で、並列実行と順次実行の混在を比較。

```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(() => $.get('nyan.txt')).then(nyan => results.nyan = nyan)
.then(() => console.log(results.hoge + results.piyo + results.nyan));
```

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

こちらも構成が似ていますね。そういう意味で`async/await`は、既存実装をより簡略化したものとして認識できるため、好感が持てます。
`async/await`側では`ES6`で導入された分割代入まで使われており、新しい技術のオンパレードです。`Promise`側もアロー演算子をやめるとティロ・フィナーレですが。

## Promise、アンタはいい子に育った。嘘もつかないし、悪いこともしない。
圧倒的と思われる`async/await`のシンプルさですが、例外が絡んでくると`Promise`の`catch`の流麗さも垣間見えます。

```js
const results = {};
Promise.resolve()
  .then(() => $.get('hoge.txt')).then(hoge => results.hoge = hoge)
  .catch(e => results.hogeError = e)
  .then(() => $.get('piyo.txt')).then(piyo => results.piyo = piyo)
  .catch(e => results.piyoError = e)
  .then(() => console.log(JSON.stringify(results)));
```

```js
(async() => {
  const results = {};
  try {
    results.hoge = await $.get('hoge.txt');
  } catch (e) {
    results.hogeError = e;
  }
  try {
    results.piyo = await $.get('piyo.txt');
  } catch (e) {
    results.piyoError = e;
  }
  console.log(JSON.stringify(results));
})();
```

次に`Promise`と`async/await`で、オブジェクトメソッドとの連携を比較。

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

```js
Promise.resolve()
  .then(Capsule.getHoge)
  .then(Capsule.getPiyo)
  .then(Capsule.out);
```

```js
(async() => {
  await Capsule.getHoge();
  await Capsule.getPiyo();
  Capsule.out();
})();
```

前回記事でも書きましたが、`Promise`はオブジェクト指向と相性が良いです。組み方によっては`Promise`にも軍配が上がります。
非同期っぽく見えないはずの`async/await`は、ここではかえって非同期感を煽っています。`await`がついている関数とついていない関数があり一体感もありません。メイン関数(ファサード部)の`await`を謎の呪文`(async() => {})();`で囲う必要があるのも悲しいところ。

## さやか、君がPromiseを覚えられない限り、杏子と戦っても、勝ち目は無いと思っていい。
こんなコードをよく見かけますよね。見かけないはずがない。

```js
// データベースから取得する
function select(key, callback) {
  db.select(key, (err, result) => {
    if (err) {
      return callback(err);
    }
    return callback(null, result);
  });
}

// データベースから取得した値をクライアントに返す
select('key', (err, result) => {
  if (err) {
    res.send('ERR:' + err);
    return;
  }
  res.send('OK:' + result);
});
```

これを`Promise`で書き換えると、こんな感じです。よくある光景です。

```js
// データベースから取得する
function select(key) {
  return new Promise((resolve, reject) => {
    db.select(key, (err, result) => {
      if (err) {
        return reject(err);
      }
      return resolve(result);
    });
  });
}

// データベースから取得した値をクライアントに返す
Promise.resolve()
  .then(() => select('key'))
  .then(result => res.send('OK:' + result))
  .catch(err => res.send('ERR:' + err));
```

これを`async/await`で書き換えると…

```js
// データベースから取得する
function select() {
  // async/awaitへの書き換えはできない
}

// データベースから取得した値をクライアントに返す
(async() => {
  try {
    const result = await select('key');
    res.send('OK:' + result);
  } catch (err) {
    res.send('ERR:' + err);
  }
})();
```

`Promise`には大きく分けて2種類あります。

* 非同期処理の終了を検知する検知側
* 非同期処理の処理順を記載する呼び出し側

呼び出し側は`Promise`を`async/await`に書き換えられますが、検知側は書き換えられません。さやかちゃん？起きて…ねぇ、ねぇちょっと、どうしたの？

## 何なんだよ、Observableって一体何なんだ！？さやかに何をしやがった！？
`Promise`では不可能なStream処理をやってくれるのが`Observable`です。`Observable`でも書いてみましょう。[同期型のmapは難しいですが](https://qiita.com/ovrmrw/items/b45d7bf29c8d29415bd7)、今回は`concatMap`を使います。

```js
const result = [];
of([])
  .pipe(
    concatMap(() => $.get('hoge.txt')),
    map(v => result.push(v)),
    concatMap(() => $.get('piyo.txt')),
    map(v => result.push(v)),
    concatMap(() => $.get('nyan.txt')),
    map(v => result.push(v))
  )
  .subscribe(() => console.log(result));
```

`Promise`の書き方とそっくりに書けました。
フロントサイドの*Angular*や*React*は全面的に`Observable`を採用しているので、たとえ`async/await`で`Promise`を撲滅しても、この書き方はまたやって来る。
第一級関数を忌み嫌う時、私達はグリーフシードになり、魔女として生まれ変わる。それが、魔法少女になった者の、逃れられない運命。

# この街にはもう一人、async functionがいるからね
さやかちゃん、`async function`は作れたって言ってるけど、でも、もしマミさんの時と同じようなことになったらって思うと。

```js
async function hoge() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('hoge'), 1000);
  });
}
async function fuga() {
  const result = await hoge();
  console.log(result);
}
fuga(); // 1秒後に'hoge'が表示される
```
はぁ…まどか、`fuga`は本来の`async function`じゃなくて、ただの抜け殻なんだって。
`hoge`が本来の`async function`で、`fuga`は`await`を使うだけための`async function`なんだよ。
これを見てごらん。

```js
async function hoge() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('hoge'), 1000);
  });
}
async function fuga() {
  const result = await hoge();
  console.log(result);
}
library.load(fuga).draw(); // draw()が即時実行されるか1秒待たされるか分からない
```
`await`で`hoge`を待ちたいという理由だけで`fuga`を`async function`にしたとしても、`library.load`がコールバックを`await`で待っていた場合、`draw`が`hoge`の終了までブロックされるという挙動に変更されるんだ。こればっかりは、`library.load`の**実装によりけり**だね。

もしブロックされたくないなら、こうやって安全策を取らなきゃいけない。

```js
async function hoge() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('hoge'), 1000);
  });
}
function fuga() {
  (async() => {
    const result = await hoge();
    console.log(result);
  })();
}
library.load(fuga).draw(); // draw()は即時実行されて1秒後に'hoge'が表示される
```
さやかちゃんはさ、怖くはないの？後悔とか全然ないの？

## 美樹さやかが一生を費やして保守しても、バグが落ち着く日は来なかった。
実際の現場では`async/await`が脆すぎる可能性はあります。
コーディング規約をちゃんとしないと、こんなことになる可能性も…。

```js
async function selectUpdate() {
  return update(await select());
}
selectUpdate()
  .then(o => res.send(o))
  .catch(e => res.send(e.message));
```

`selectUpdate`が開発途中で`async function`に変更されても受ける側は`then`のままだったり、`await`と混在してしまったり…というのはマシな方です。
恐いのは`update`関数の呼び出しに`await`がついてないのが、ミスかどうかすぐには分からないところ。今は大丈夫でも`update`関数の仕様が途中で変更される可能性もあります。もしこれがバグの原因となり品質管理チームにバレてしまうと、「**関数呼び出し時には必ず`await`をつける**」というコーディング規約が爆誕し、多くの犠牲者が出るかもしれません。
そんなルールは有り得ないと言いたいところですが、悲しいことに`Promise`の`then`による関数呼び出しは全て「**`await`をつけて呼び出している状態**」になっており、それはある意味で**安全**なのです。

`Promise.all`であえて非同期にしない限り、`Promise`で書くと全て同期処理された。だけど`async/await`にすると、`await`の付け方で思いもよらない非同期処理が生まれてしまう。私達魔法少女って、そう言う仕組みだったんだね。

あたしって、ホントばか。

おしまい