11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-06-15

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

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

準備

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

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: 待ち合わせしない場合

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

コード

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

実行結果

$ node index.js
A: 1.533ms

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

B: 待ち合わせする場合

コード

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

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

実行結果

$ node index.js
B: 1005.785ms

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

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

コード

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

実行結果

$ node index.js
C: 3.737ms

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

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

コード

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

実行結果

$ node index.js
D: 2009.503ms

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

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

コード

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");
})();

実行結果

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

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

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

コード

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");
})();

実行結果

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

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

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

コード

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");
})();

実行結果

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

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

H: 補足1 (順次処理)

コード

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");
})();

実行結果

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

H: 補足2 (並行処理)

コード

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");
})();

実行結果

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

まとめ

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

そーすこーど

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");
})();
11
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?