呼吸好音樂:網易氧氣耳機 49 元新低(新增 Type-C 版)
1. js同步與非同步
-
Js作為瀏覽器指令碼語言,它的主要用途是與使用者互動,以及操作DOM,因此js是單執行緒,也避免了同時操作一個DOM的矛盾問題
- 單執行緒就意味著,所有任務需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就不得不一直等著。
-
Js引擎存在monitoring process程序,會持續不斷的檢查主執行緒執行棧是否為空,一旦為空,就會去Event Queue那裡檢查是否有等待被呼叫的函式,這個過程是迴圈不斷的,所以整個的這種執行機制又稱為Event Loop(事件迴圈)
- 如果js只有同步任務,程式碼是一行一行從上往下執行的,其中一行程式碼解析過程中耗時較長會導致後面程式碼無法繼續執行頁面,等待前面程式碼執行結束才能繼續向下解析,會帶來不好的使用者體驗
- 所以出現了同步與非同步任務:同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;非同步任務指的是,不進入主執行緒、而進入"任務佇列"(task queue)的任務,只有"任務佇列"通知主執行緒,某個非同步任務可以執行了,該任務才會進入主執行緒執行。
- 其中非同步任務又分為巨集任務和微任務
Event Loop事件迴圈:
因此任務執行過程如下:
-
整體會把所有程式碼分為兩個部分:‘同步任務’,‘非同步任務’
- 所有同步任務都在主執行緒上執行,形成一個執行棧
- 主執行緒之外還存在一個任務佇列,專門存放非同步任務(巨集任務和微任務)
-
巨集任務進入到Event Table中,並在裡面註冊回撥函式,每當指定的事件完成時,Event Table會將這個函式移到Event Queue中
-
微任務也會進入到另一個Event Table中,並在裡面註冊回撥函式,每當指定的事件完成時,Event Table會講這個函式移到Event Queue中
-
當主執行緒的任務執行完畢,主執行緒為空時,會檢查微任務的Event Queue,如果有任務,就會全部執行,如果沒有就執行下一個巨集任務
- 主執行緒不斷重複上面的步驟,這就是Event Loop事件迴圈,只要主執行緒空了,就會去讀取"任務佇列"。這個過程會不斷重複。(同步>微任務>巨集任務)
2. js巨集任務與微任務
這裡需要注意的是new Promise是會進入到主執行緒中立刻執行,而promise.then則屬於微任務,微任務的層級大於巨集任務(先執行微任務再執行巨集任務)
巨集任務:script、setTimeOut、setInterval、I/O等
微任務:promise.then、process.nextTick(node),Object.observe,MutationObserver
3. 案例:
1.
列印結果:1,3,5,4,2
因為以同步非同步的方式來解釋執行機制是不準確的,非同步還分為巨集任務和微任務:
因此執行機制便為:雖然先執行的是巨集任務(setTimeout)被放到巨集任務佇列中,繼續往下走看有沒有微任務,檢測到Promise的then函式把他放入了微任務序列,等到主線程序的所有程式碼執行結束後。先從微任務裡拿回調函式,然後微任務空了後再從巨集任務的拿函式。
2.
列印結果:
同步巨集任務promise、同步巨集任務、同步微任務then、非同步巨集任務promise、非同步巨集任務、非同步微任務then
從上往下執行,setTimeout巨集任務放到巨集任務佇列(不管執行時間是0還是10000,setTimeout是巨集任務必須等到主執行緒的所有程式碼執行結束才能執行),new Promise會立刻進入主執行緒執行,列印“同步巨集任務promise”,遇到promise.then放到微任務佇列,列印“同步巨集任務”,執行完畢重新從上往下執行,列印“同步微任務then",執行結束重新從上往下執行,此時主執行緒已經執行完畢,微任務也執行完畢,輪到setTimeout巨集任務開始執行,new Promise會立刻進入主執行緒執行,列印“非同步巨集任務promise”,遇到promise.then放到微任務佇列,列印“非同步巨集任務”,此時主執行緒任務全部執行完畢,開始執行promise.then列印“非同步微任務then”
3.
列印結果:script start、async1 statrt、async2、promise1、script end、asnyc1 end、promise2
從上往下執行,首先newPromise是同步任務,會被放到主程序中去立即執行,而.then()函式是非同步任務會放到非同步佇列中去,(當你的promise狀態結束的時候,就會立即放進非同步佇列中去),到async關鍵字的函式會返回一個promise物件,如果裡面沒有await,執行起來等同於普通函式,await關鍵字要在async關鍵字函式的內部,await寫在外面會報錯,await如同他的語義,就是在等待,等待右側的表示式完成,此時的await會讓出執行緒,阻塞async內後續的程式碼,先去執行async外的程式碼,等外面的同步程式碼執行完畢,才會執行裡面的後續程式碼,就算await的不是promise物件,是一個同步函式,也會等這樣的操作