前提・実現したいこと
javascriptのasync/awaitを使用するプログラムを理解する必要があり、色々とプログラムを動かしていたのですが、自分と考えている挙動と異なるasync, awaitの挙動が有り、どうしてそうなるのか分かっていません。
試したこと・質問1
javascript
1function asyncFunc() { 2 return new Promise((resolve => { 3 console.log(3); 4 resolve("he"); 5 })); 6} 7 8async function wait() { 9 console.log(2); 10 let a = await asyncFunc().then((val) => { 11 console.log(val); 12 return val + " likes"; 13 }); 14 console.log(a); 15 console.log(5); 16} 17 18console.log(1); 19wait(); 20console.log(4);
以上のコードを実行すると
1 2 3 4 he helikes 5
のようになりました。
これが起きる理由として
- 1がコンソールへ
- wait関数がスタックに積まれる
- 2がコンソールへ
- asyncFuncの実行でスタックに積む
- 3がコンソールへ
- resolve("he")をして、PromisがFulfill状態になる。
- await asyncFunc()のようになっているため、強制的にこのwait関数のasync移行はキューに積まれる。
- wait関数はキューに写ったので、4をコンソールへ。
- スタックがからのため、キューに戻ってくる。
- asyncFuncの返り値のFulFilで設定したconsole.valを実行(he)
- return val + "likes"で、"he likes"オブジェクトがPromisenoFulFil状態で帰る。
- aのawaitが終了し、he likesとなり、コンソールへ
- 最後に5を出力する。
のように考えています。
ここで、考えた私の仮説として
async関数内でawaitを使用すると、await内でなにか非同期処理が生じた瞬間、それ以降の内容は強制的にキューにぶち込み、スタックが空になってから実行する。
上記のルールがあるため、3を出力してresolveをした後(resolveは非同期処理のため)、これら以降のwait関数の処理を飛ばし、先に外側の4を出力しているのだと考えました。
質問として、上記のルールは正しいのでしょうか?
試したこと・質問2
2はかなり非同期処理の挙動が複雑になっています。
async function asyncFunction(time) { console.log("asyncFuntion" + time) return time; } async function hoge() { return 0; } async function hen(name) { let j = 0.99; console.log("nyan " + name); let c = await hoge(); console.log("in hen " + name); c = await hoge(); for (let i = 0; i <= 100000000; i++) j *= 0.99; console.log("out hen " + name); return j; } async function rapper() { // awaitはそれ移行全部キューに入れる let arr = [await asyncFunction(0).then((val) => { return val * 20 }), asyncFunction(1)]; console.log(await Promise.all(arr)) } rapper(); console.log("here"); console.log(hen("global")); console.log("nannde");
以上のコードを実行したところ
asyncFuntion0 here nyan global Promise {pending} nannde in hen global asyncFuntion1 out hen global (2) [0, 1]
のようになりました。
これの挙動は
- rapper関数を実行する
- let arr内のawait asyncFunctionを実行する
- コンソールにasyncFunction 0
- awaitが実行されたので、強制的にこれ以降(awaitFunction(0)以降)を、スタックからキューに写す。
- コンソールにhere
- hen("global")を実行する
- nyan globalをコンソールに
- await hogeをする。ここで、awaitが実行されたので、強制的にこれ以降(let c = await hoge()以降)を、スタックからキューに写す。
- グローバルに戻ってきて、コンソールにhen("global")のPending状態のPromiseを出力する。
- nanndeをコンソールに出力する
- ここでどうしてかhen関数内に戻る(キューの追加順を考えれば、rapperに戻らないとおかしい。)
- in hen globalを出力する
- await hogeをおこない、強制的に抜け出る。
- キューからrapperを取り出して、asyncFunction(1)を実行し、asyncFunction1を実行する
- await Promise.allをする。よって、強制的に抜け出る。
- for(let i = 0;i<=10000000;i++)の重い処理が同期処理される。
- out hen globalを出力する。
- 最後に(2)[0, 1]を出力する。
であると考えています。
ここで気になった質問なのですが
2-1. awaitキーワードがあった時点で内部関数を実行し、その内部関数で非同期処理の内容があった瞬間、それ以降をすべてキューに移し、他のスタック処理を行うという認識はあっていますでしょうか?(挙動の4, 8などです)
2-2. 11でhen関数内に戻っていて、キューの追加順を考えると、hen関数内に戻るのはおかしいと考えているのですが、これはキューに追加されていないということなのでしょうか?
2-3. async, await, setTimeoutの挙動は同じなのでしょうか?(非同期処理という意味で一概に同じグループにして大丈夫でしょうか)
回答3件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。