async/await理解のためのクイズを考えてみた (前半)
はじめに
今私は、JSの非同期処理を100%理解したいと思い、以下の学習ロードマップを参考にして学習を進めています。
JSの非同期処理を理解するために必要だった知識と学習ロードマップ
今回は、その学習ロードマップの中の「Promise チェーンの構築のアンチパターンを学ぶ」という章で紹介されていた以下の動画に取り組んでいきます。
JavaScriptのasync/await 理解してますか? 説明できますか? クイズに答えてもらって良いですか?
今回は、動画の前半くらいまでのアンチパターンを修正するというところ考えてみます。
アンチパターン
以下のコードを正しい書き方にしろという問題です。
//node14
const test = async() => {
console.log(1);
await setTimeout(() => {
console.log(2);
}, 1000)
}
const main = async() => {
await test();
console.log(3);
}
main();
何が問題か
答えを聞く前に自分で考えてみた記録を書きます。
私の予想
1
3
2
- mainという関数が呼ばれる
- main関数の中でtest関数をawaitで呼ぶ。(このtest関数の結果を待って、次の処理に行く)
-
1
が出力 - setTimeoutの処理が始まるが、非同期コールバック関数であるため、この処理の結果はまだ出力せずに、他の処理を実行させる。
- test関数の
3
を出力 - setTimeoutの処理の
3
が出力
でも引っかかるのが、main関数はtest関数の結果を待つはずだが、test関数内のsetTimeoutの処理が終わってない。
回答
予想は合っていた。
1
3
2
やはり、setTimeoutにawaitをつけたことによって、他の処理に実行終了を待って、setTimeoutの処理が行われるみたい。 (👉のちにこの解釈は誤解があることがわかる)
node14では、setTimeoutにawaitをつけるべきではないとあるらしい。
修正したらどうなる?
1,2,3に出力がなるように、修正します。
私の予想
いくつか予想を立ててみた
1つ目
//node14
const test = function () {
console.log(1);
setTimeout(() => {
console.log(2);
}, 1000)
}
const main = async() => {
await test();
console.log(3);
}
main();
ただsetTimeoutからawaitを抜いただけ。setTimeoutは非同期コールバック関数で、task queueに一度入るよね。でもasync awaitは多分、microtask queueに入る非同期コールバック関数だから、setTimeoutよりも実行が優先されて、1, 3, 2になるのか…?
自分の環境で実行してみたら、出力がやっぱり、1,3,2になった。
てことは、同期コールバック関数をsetTimeoutの代わりに書けばいいのか…?
//node14
const test = function () {
console.log(1);
console.log(2);
}
const main = async() => {
await test();
console.log(3);
}
main();
それか
//node14
const array = [1, 2]
const test = function () {
array.forEach((element) => console.log(element));
}
const main = async() => {
await test();
console.log(3);
}
main();
これでいいのかって感じだけど、同期コールバック関数に置き換えたことによって、出力は1,2,3になった。でも非同期処理の本質的な問題からはズレている気がする。
さあ、正解を見てみよう
回答
// node14
const test = async () => {
console.log(1);
await new Promise((resolve) => {
setTimeout(() => {
console.log(2);
resolve();
}, 1000);
})
}
const main = async() => {
await test();
console.log(3);
}
うわあ、ここでPromise使うんかあ…。わかってたつもりだったのに悔しい〜。
なんでこれが正解なのか考えてみる
Promise処理を書いていて、その中にsetTimeout関数がある。このPromise処理では結果をresolveで伝えている。その結果を待って、3
が出力されているということか。
まとめ
-
awaitは、Promiseだけを待機できる。
👉 ここ知ってたようで、知らなかった。なんでも待てるのかと思ってた。 - setTimeoutは、Promiseを返さないから、test関数内で実行はされるけど、awaitでは止まらない。
Discussion