【JavaScript】async/await は文法覚えるだけじゃなくて並行処理を書けるようになろうねというお話

async/await の文法は覚えたけど、まだ並行処理には頭が回っていないという人に向けて。
（いんたねっと見てると 「`async function()` は 必ず `await` で待ち受ける」と機械的に覚えちゃってる人が多そうな印象があったので。勘違いだったらごめんなさい:pray_tone1:）

以下、順を追って Promise の挙動と順次処理・並行処理を見ていきます。

# 準備

`sleep()` と `sleepTime()` という関数を作ります。

```js:index.js
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function sleepTime(ms) {
  await sleep(ms);
  return ms;
}
```

`sleep()` はいわゆる sleep で、引数で与えたミリ秒分処理を停止します。
`sleepTime()` は引数で与えたミリ秒分停止した後、その数値を返却します。

# A: 待ち合わせしない場合

関数を作成したのと同じファイルに即時実行関数を配置して動作を確認していきます。

## コード

```js:index.js
(async () => {
  console.time("A");
  sleep(1000);
  console.timeEnd("A");
})();
```

## 実行結果

```sh
$ node index.js
A: 1.533ms
```

1000ミリ秒を引数で指定していますが、Promiseの完了を待ち合わせていないためそのまま処理が進んで `console.timeEnd()` が実行されています。

# B: 待ち合わせする場合

## コード

```js:index.js
(async () => {
  console.time("B");
  await sleep(1000);
  console.timeEnd("B");
})();
```

`sleep()` で返却されるPromiseの完了を `await` で待ちます。

## 実行結果

```sh
$ node index.js
B: 1005.785ms
```

先ほどとは違いしっかり1000ミリ秒 sleep しています。

# C: 待ち合わせしない場合 (複数)

## コード

```js:index.js
(async () => {
  console.time("C");
  sleep(400);
  sleep(600);
  sleep(1000);
  console.timeEnd("C");
})();
```

## 実行結果

```sh
$ node index.js
C: 3.737ms
```

Promise の完了より先に `console.timeEnd()` が呼ばれます。

# D: 待ち合わせする場合 (複数)

## コード

```js:index.js
(async () => {
  console.time("D");
  await sleep(400);
  await sleep(600);
  await sleep(1000);
  console.timeEnd("D");
})();
```

## 実行結果

```sh
$ node index.js
D: 2009.503ms
```

400 + 600 + 1000 = 2000ミリ秒となっています。

# E: 戻り値がある場合 (待ち合わせなし)

## コード

```js:index.js
(async () => {
  console.time("E");
  const a = sleepTime(400);
  const b = sleepTime(600);
  const c = sleepTime(1000);
  console.log(a, b, c);
  console.timeEnd("E");
})();
```

## 実行結果

```sh
$ node index.js
Promise { <pending> } Promise { <pending> } Promise { <pending> }
E: 20.878ms
```

Promise の完了を待っていないのがわかります。

# F: 戻り値がある場合 (順次処理)

## コード

```js:index.js
(async () => {
  console.time("F");
  const d = await sleepTime(400);
  const e = await sleepTime(600);
  const f = await sleepTime(1000);
  console.log(d, e, f);
  console.timeEnd("F");
})();
```

## 実行結果

```sh
$ node index.js
400 600 1000
F: 2049.220ms
```

Promise の完了を同期的に待っているのがわかります。
400 + 600 + 1000 = 2000ミリ秒。

# G: 戻り値がある場合 (並行処理)

## コード

```js:index.js
(async () => {
  console.time("G");

  // Promise を変数に格納して一旦泳がせる
  const g = sleepTime(400);
  const h = sleepTime(600);
  const i = sleepTime(1000);

  // 全ての Promise を泳がせてからまとめて待ち合わせ
  const j = await g;
  const k = await h;
  const l = await i;

  console.log(g, h, i, j, k, l);
  console.timeEnd("G");
})();
```

## 実行結果

```sh
$ node index.js
Promise { 400 } Promise { 600 } Promise { 1000 } 400 600 1000
G: 1033.181ms
```

待ち合わせを行ったため全てのPromiseが完了しています。
が、並行処理を行ったため実行時間は2000ミリ秒ではなく1000ミリ秒となっています。

# H: 補足1 (順次処理)

## コード

```js:index.js
(async () => {
  console.time("H");
  const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  for (const num of arr1) {
    console.log(await sleepTime(num * 100));
  }
  console.timeEnd("H");
})();
```

## 実行結果

```sh
$ node index.js
100
200
300
400
500
600
700
800
900
1000
H: 5595.286ms
```

# H: 補足2 (並行処理)

## コード

```js:index.js
(async () => {
  console.time("I");
  const arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  // Promise を一旦泳がせる
  const promises = arr2.map(num => sleepTime(num * 100));

  for (const promise of promises) {
    console.log(await promise);
  }
  console.timeEnd("I");
})();
```

## 実行結果

```sh
$ node index.js
100
200
300
400
500
600
700
800
900
1000
I: 1007.499ms
```

# まとめ

同期する必要のない Promise はまとめて泳がせて、まとめて待ち合わせて、並行処理を使っていきましょう:thumbsup_tone1:

# そーすこーど

```js:index.js
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function sleepTime(ms) {
  await sleep(ms);
  return ms;
}

(async () => {
  console.time("A");
  sleep(1000);
  console.timeEnd("A");

  console.time("B");
  await sleep(1000);
  console.timeEnd("B");

  console.time("C");
  sleep(400);
  sleep(600);
  sleep(1000);
  console.timeEnd("C");

  console.time("D");
  await sleep(400);
  await sleep(600);
  await sleep(1000);
  console.timeEnd("D");

  console.time("E");
  const a = sleepTime(400);
  const b = sleepTime(600);
  const c = sleepTime(1000);
  console.log(a, b, c);
  console.timeEnd("E");

  console.time("F");
  const d = await sleepTime(400);
  const e = await sleepTime(600);
  const f = await sleepTime(1000);
  console.log(d, e, f);
  console.timeEnd("F");

  console.time("G");
  // Promise を変数に格納して泳がせる
  const g = sleepTime(400);
  const h = sleepTime(600);
  const i = sleepTime(1000);
  // 全ての Promise を泳がせてからまとめて待ち合わせ
  const j = await g;
  const k = await h;
  const l = await i;
  console.log(g, h, i, j, k, l);
  console.timeEnd("G");

  console.time("H");
  const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  for (const num of arr1) {
    console.log(await sleepTime(num * 100));
  }
  console.timeEnd("H");

  console.time("I");
  const arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  const promises = arr2.map(num => sleepTime(num * 100));
  for (const promise of promises) {
    console.log(await promise);
  }
  console.timeEnd("I");
})();
```