1. 程式人生 > >React 伺服器渲染原理解析與實踐(已完結)2018(最全)

React 伺服器渲染原理解析與實踐(已完結)2018(最全)

之前我的認知是,這是一個generator的語法糖,是用來解決非同步問題的,看起來寫起來是同步程式碼,但實際執行還是非同步的,感覺和他說的有點偏差,有偏差就說明至少有個人錯了;於是我打算重新認識一遍這個東西,說不定會有新收穫。

TALK IS CHEAP, SHOW YOU THE CODE.

async function async1(){
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

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

console.log('script start');

setTimeout(function(){
    console.log('setTimeout');
},0);

async1();

new Promise(function(resolve){
    console.log('promise1');
    resolve();
}).then(function(){
    console.log('promise2');
});

console.log('script end');
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
按照我之前的理解,我會把答案寫成:

script start
async1 start
async2
async1 end
promise1
script end
promise2
setTimeout
因為我想await async2這裡就是會等待他返回然後再往下執行

然而其實答案是:

script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout
這裡就涉及到await的特點了:

await 表示式會暫停當前 async function 的執行,等待 Promise 處理完成。若 Promise 正常處理(fulfilled),其回撥的resolve函式引數作為 await 表示式的值,繼續執行 async function。
若 Promise 處理異常(rejected),await 表示式會把 Promise 的異常原因丟擲。
另外,如果 await 操作符後的表示式的值不是一個 Promise,則返回該值本身。

這裡的暫停執行,我們應該理解為交出執行緒控制權,去執行後面的函式。我們反向來看,假如async函式裡面一直等待await後面的表示式返回,那整個執行緒就等於卡在這個async函式裡了,那不就變成同步阻塞的了。

所以我們回到上面的那段程式碼,開始分析
首先程式碼同步執行輸出 script start ,接著 setTimeout 中的回撥屬於 MacroTask 佇列,會被推入該佇列等待執行。
接著程式碼執行到 async1 函式裡面,輸出 async1 start,接著遇到了await關鍵字,這一行語句會從右到左執行,先執行 async2 函式,輸出async2,因為這個函式沒有返回值,所以返回了一個 Promise.resolve(undefined).然後這個 Promise 會被推入 MicroTask 佇列,同時 await 讓出執行緒,跳出 async1 函式,繼續執行下面的同步程式碼。
Promise構造器中的函式引數會被立即執行,所以輸出 promise1 ,然後執行了resolve(),這個 Promise 也被推入 MicroTask 佇列等待執行,接著輸出script end,好了,同步程式碼執行完了,呼叫棧現在空了。
然後開始處理非同步的任務佇列。第一個是 async2 中返回的Promise.resolve(undefined)被resolve之後再次加入佇列,所以回撥沒被執行,第二個是 new Promise裡resolve的 Promise ,其回撥被執行,輸出 promise2 。好的現在呼叫棧又空了,然後開始再一次處理非同步佇列,這次取到的是之前被第二次放進來的Promise的回撥,執行之後, 表示式await async2()的求值就完成了,值是undefined。接著可以認為async2返回的Promise已經處理完了,接著執行下面的程式碼,輸出async1 end。
最後就是MacroTask佇列中的回撥被執行,輸出setTimeout

這裡需要解釋一下一個地方,async2 返回的Promise狀態是Resolved,為什麼會被放到任務佇列裡面兩次。首先async2函式是一個async function,他的返回值是一個resolved狀態的Promise,await async2()類似於await Promise.resolve(something),Chrome的V8引擎對這個過程進行了優化,把Promise.resolve()的返回值優化成了返回一個新的Promise,這樣他的回撥就不會被立即執行,而是需要再等一個迴圈才執行。而在Node裡面,Promise.resolve返回的就是當前的Promise,回撥會被立即執行,所以如果你在node裡面執行的話,順序會是這樣的:

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
是因為Node環境下,async2返回的Promise在第一次被推入佇列時就是resolved的,跟new Promise那個是一樣的,所以按照入佇列的順序,先輸出async1 end。這一點差異,應該以最新V8引擎為準。
--------------------- 
作者:divasatanica 
來源:CSDN 
原文:https://blog.csdn.net/divasatanica/article/details/85109924 
版權宣告:本文為博主原創文章,轉載請附上博文連結!