1. 程式人生 > >理解 async/await 的執行

理解 async/await 的執行

這是一篇簡單的短文章,方便理解。

開局先丟官宣:sec-async-function-definitions 這個連結是對 await 的解釋,解釋了它的執行。

await 的執行意味著(官宣巴拉巴拉地說了14點,這裡簡化成2點):

1. await 以 promise 形式完成,且 await 之後的程式碼在 promise 被完成後以 .then 執行。

2. 遇到 await 時,將 async上下文 從上下文堆疊中移除,將上級(async之外)上下文(依次)恢復為當前執行的上下文;這步過程中需設定async上下文的評估狀態,以便在恢復到執行上下文時呼叫 await 之後的步驟。

第2點類似的理解為 async上下文 被掛起,依次向上執行上下文堆疊中的上下文程式碼,完了後執行 async 函式裡 await 之後的程式碼。

估計看的繞,用程式碼說話吧。

例一

複製程式碼

console.log(1);
async function async1(){ 
    console.log(2); 
    await async2(); 
    console.log(3);
};

async function async2(){ console.log(4)};

async1();
console.log(5);

// 輸出結果 1 2 4 5 3

複製程式碼

理解一下輸出結果:

第一步:輸出 1;

第二步:輸出 2,async1 先被呼叫,肯定優先於函式內的console和後面的console.log(5);

第三步:輸出 4,把 await async2() 轉為 promise 形式:

複製程式碼

    new Promise(resolve => {
        console.log('2')
        resolve();
    }).then(res => {
        // 抽出 await 之後的程式碼放到.then
        console.log(3);
    });

複製程式碼

這時候先輸出 2,再等 3 的輸出。

但由於 3 在Promise規範裡被設定為非同步(劃重點: ES6中屬microTask ,此處直接影響低版本中對promise實現的shim ),且await表示遇到await關鍵字後先將這塊程式碼的asyncContext掛起並執行上級上下文,所以先執行了5,於是...

第四步:輸出 5,執行完後回到 async context,再於是...

第五步:最後輸出 3。

例二

在官宣上沒看到  依次向上  字樣鴨,咋肥事?繼續程式碼:

複製程式碼

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);
// 輸出結果 1 2 4 6 7 8 3 5

複製程式碼

理解一下輸出結果:

第一步:輸出 1;

第二步:fn1 被執行,fn2 被執行,輸出 2;

第三步:fn3 被執行,如第一例所述,輸出 4;

第四步:async context 被掛起,將 fn2 上下文作為執行上下文,輸出 6;

第五步:fn2 上下文處理後繼續向外更新,fn1 上下文作為執行上下文,輸出 7;

第六步:重複上述,輸出 8;

第七步:由於 fn3 的await(promise)在 fn2 中的 new Promise 前被加入執行列隊,根據先進先出的執行順序,輸出 3;

第八步:最後輸出 5。

例三

如果2個 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

複製程式碼

重複上一個理解的過程,把 await async1(); 後的程式碼放到最後執行,看做:

複製程式碼

new Promise(resolve => {
    // async1 裡的程式碼
    resolve();
}).then(res => {
       new Promise(resolve => resolve(5)).then(res => console.log(res));
    console.log(6); 
});

複製程式碼

對比以上輸出結果,正確!

如果有多個或者巢狀promise,則以  await 變成promise並掛起async上下文等上級上下文執行完後恢復  和  事件的執行順序遵循先進先出  兩個規則去完成推斷執行順序。

注意

1. 在低版本瀏覽器中,async/await也有存在一些執行順序的偏差(或者根本就不支援);

2. 在ES6和ES5中promise的執行也有不同點(上述提到,ES6中promise屬microtask;在ES5中,暫未接觸到有api直接操作microtask的,所以.then的非同步是用setTimeout代替,屬macrotask,導致輸出有差異);關於promise也可參考上文 Promise 拆分理解和組裝

3. 由於客戶端環境不可控性太高,建議用於nodejs端開發。

彩蛋

通過上面的理解,再看下面的圖片(這是koa middleware的... 嘿嘿嘿):

 

希望這篇筆記能夠幫助前端袍澤們對async await的執行過程有更好的理解。上述內容僅供參考,如有理解不當的地方,還望提出!

瀋陽無痛胃鏡醫院

瀋陽胃鏡哪個醫院好

鄭州正規婦科醫院

鄭州哪家不孕不育醫院好