理解 async/await 的执行
這是一篇簡(jiǎn)單的短文章,方便理解。
開(kāi)局先丟官宣:sec-async-function-definitions?這個(gè)鏈接是對(duì) await 的解釋,解釋了它的執(zhí)行。
await 的執(zhí)行意味著(官宣巴拉巴拉地說(shuō)了14點(diǎn),這里簡(jiǎn)化成2點(diǎn)):
1. await 以 promise 形式完成,且 await 之后的代碼在 promise 被完成后以 .then 執(zhí)行。
2. 遇到 await 時(shí),將 async上下文 從上下文堆棧中移除,將上級(jí)(async之外)上下文(依次)恢復(fù)為當(dāng)前執(zhí)行的上下文;這步過(guò)程中需設(shè)置async上下文的評(píng)估狀態(tài),以便在恢復(fù)到執(zhí)行上下文時(shí)調(diào)用 await 之后的步驟。
第2點(diǎn)類(lèi)似的理解為 async上下文 被掛起,依次向上運(yùn)行上下文堆棧中的上下文代碼,完了后執(zhí)行 async 函數(shù)里 await 之后的代碼。
估計(jì)看的繞,用代碼說(shuō)話吧。
例一
console.log(1); async function async1(){ console.log(2); await async2(); console.log(3); };async function async2(){ console.log(4)};async1(); console.log(5);// 輸出結(jié)果 1 2 4 5 3理解一下輸出結(jié)果:
第一步:輸出 1;
第二步:輸出 2,async1 先被調(diào)用,肯定優(yōu)先于函數(shù)內(nèi)的console和后面的console.log(5);
第三步:輸出 4,把 await async2() 轉(zhuǎn)為 promise 形式:
new Promise(resolve => {console.log('2')resolve();}).then(res => {// 抽出 await 之后的代碼放到.thenconsole.log(3);});這時(shí)候先輸出 2,再等 3 的輸出。
但由于 3 在Promise規(guī)范里被設(shè)定為異步(劃重點(diǎn): ES6中屬microTask ,此處直接影響低版本中對(duì)promise實(shí)現(xiàn)的shim ),且await表示遇到await關(guān)鍵字后先將這塊代碼的asyncContext掛起并執(zhí)行上級(jí)上下文,所以先執(zhí)行了5,于是...
第四步:輸出 5,執(zhí)行完后回到 async context,再于是...
第五步:最后輸出 3。
例二
在官宣上沒(méi)看到? 依次向上??字樣鴨,咋肥事?繼續(xù)代碼:
console.log(1); function fn1(){function fn2(){async function async1(){ console.log(2);await fn3();console.log(3);}function fn3(){ console.log(4); }async1();new Promise(resolve => resolve(5)).then(res => console.log(res));console.log(6);}fn2();console.log(7); } fn1(); console.log(8); // 輸出結(jié)果 1 2 4 6 7 8 3 5理解一下輸出結(jié)果:
第一步:輸出 1;
第二步:fn1 被執(zhí)行,fn2 被執(zhí)行,輸出 2;
第三步:fn3 被執(zhí)行,如第一例所述,輸出 4;
第四步:async context 被掛起,將 fn2 上下文作為運(yùn)行上下文,輸出 6;
第五步:fn2 上下文處理后繼續(xù)向外更新,fn1 上下文作為運(yùn)行上下文,輸出 7;
第六步:重復(fù)上述,輸出 8;
第七步:由于 fn3 的await(promise)在 fn2 中的 new Promise 前被加入執(zhí)行列隊(duì),根據(jù)先進(jìn)先出的執(zhí)行順序,輸出 3;
第八步:最后輸出 5。
例三
如果2個(gè) async 嵌套順序是啥樣的呢?再看代碼:
console.log(1); function fn1(){async function fn2(){async function async1(){ console.log(2);await fn3();console.log(3);}function fn3(){ console.log(4); }await async1();new Promise(resolve => resolve(5)).then(res => console.log(res));console.log(6);}fn2();console.log(7); } fn1(); console.log(8); // 1 2 4 7 8 3 6 5重復(fù)上一個(gè)理解的過(guò)程,把 await async1(); 后的代碼放到最后執(zhí)行,看做:
new Promise(resolve => {// async1 里的代碼 resolve(); }).then(res => {new Promise(resolve => resolve(5)).then(res => console.log(res));console.log(6); });對(duì)比以上輸出結(jié)果,正確!
如果有多個(gè)或者嵌套promise,則以??await 變成promise并掛起async上下文等上級(jí)上下文執(zhí)行完后恢復(fù)? 和? 事件的執(zhí)行順序遵循先進(jìn)先出? 兩個(gè)規(guī)則去完成推斷執(zhí)行順序。
注意
1. 在低版本瀏覽器中,async/await也有存在一些執(zhí)行順序的偏差(或者根本就不支持);
2. 在ES6和ES5中promise的執(zhí)行也有不同點(diǎn)(上述提到,ES6中promise屬microtask;在ES5中,暫未接觸到有api直接操作microtask的,所以.then的異步是用setTimeout代替,屬macrotask,導(dǎo)致輸出有差異);關(guān)于promise也可參考上文?分步理解 Promise 的實(shí)現(xiàn)
3. 由于客戶端環(huán)境不可控性太高,建議用于nodejs端開(kāi)發(fā)。
彩蛋
通過(guò)上面的理解,再看下面的圖片(這是koa middleware的... 嘿嘿嘿):
?
希望這篇筆記能夠幫助前端袍澤們對(duì)async await的執(zhí)行過(guò)程有更好的理解。上述內(nèi)容僅供參考,如有理解不當(dāng)?shù)牡胤?#xff0c;還望提出!
總結(jié)
以上是生活随笔為你收集整理的理解 async/await 的执行的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 算法62---最长回文子序列长度(子串)
- 下一篇: [性能优化]UITableView性能优