async/await の文法は覚えたけど、まだ並行処理には頭が回っていないという人に向けて。
(いんたねっと見てると 「async function()
は 必ず await
で待ち受ける」と機械的に覚えちゃってる人が多そうな印象があったので。勘違いだったらごめんなさい)
以下、順を追って Promise の挙動と順次処理・並行処理を見ていきます。
準備
sleep()
と sleepTime()
という関数を作ります。
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleepTime(ms) {
await sleep(ms);
return ms;
}
sleep()
はいわゆる sleep で、引数で与えたミリ秒分処理を停止します。
sleepTime()
は引数で与えたミリ秒分停止した後、その数値を返却します。
A: 待ち合わせしない場合
関数を作成したのと同じファイルに即時実行関数を配置して動作を確認していきます。
コード
(async () => {
console.time("A");
sleep(1000);
console.timeEnd("A");
})();
実行結果
$ node index.js
A: 1.533ms
1000ミリ秒を引数で指定していますが、Promiseの完了を待ち合わせていないためそのまま処理が進んで console.timeEnd()
が実行されています。
B: 待ち合わせする場合
コード
(async () => {
console.time("B");
await sleep(1000);
console.timeEnd("B");
})();
sleep()
で返却されるPromiseの完了を await
で待ちます。
実行結果
$ node index.js
B: 1005.785ms
先ほどとは違いしっかり1000ミリ秒 sleep しています。
C: 待ち合わせしない場合 (複数)
コード
(async () => {
console.time("C");
sleep(400);
sleep(600);
sleep(1000);
console.timeEnd("C");
})();
実行結果
$ node index.js
C: 3.737ms
Promise の完了より先に console.timeEnd()
が呼ばれます。
D: 待ち合わせする場合 (複数)
コード
(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: 戻り値がある場合 (待ち合わせなし)
コード
(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: 戻り値がある場合 (順次処理)
コード
(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: 戻り値がある場合 (並行処理)
コード
(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 (順次処理)
コード
(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 (並行処理)
コード
(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 はまとめて泳がせて、まとめて待ち合わせて、並行処理を使っていきましょう
そーすこーど
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");
})();