Javascript事件迴圈——Event loop
引言
Javascript是一門單執行緒的指令碼語言,無法進行多執行緒程式設計; 因此為了不阻塞程式設計,Javascript通過事件迴圈的方式解決耗時任務,實現類多執行緒程式設計;
單執行緒
單執行緒意味著在瀏覽器中,同一時間只能做一件事,其他的行為和事件都會在事件佇列中排隊;
Javascript單執行緒的特性與其用途有關;作為瀏覽器指令碼語言,我們需要進行各種DOM操作,如果Javascript是多執行緒的,當兩個執行緒同時操作同一DOM,一個向其新增事件,一個要刪除此節點,瀏覽器該以哪個執行緒為準呢。
HTML5 webworker技術,允許Javascript指令碼建立多個執行緒,但子執行緒完全受主執行緒控制,且不得操作DOM,未能改變其單執行緒的本質。
執行環境 Execution Context
每當程式的執行流入到一個可執行的程式碼區塊時,就進入到一個執行環境中;
當Javascript被瀏覽器載入後,預設最先進入的是全域性執行函式,之後,函式的每次呼叫都會新建執行環境;
執行環境棧 Execution stack
執行流依次進入的執行環境在邏輯上形成一個棧,棧的底部總是全域性執行環境;棧的頂部是處於活動狀態的當前的執行環境;
每個函式都有自己的執行環境,當執行流進入一個函式時,函式的環境會被推入一個環境棧中,函式執行完成後,棧將其環境彈出,把控制權還給之前的執行環境;
注意每次函式呼叫都會建立一個執行環境壓入棧中,無論是函式內部的函式還是遞迴;
事件迴圈
執行機制
- 事件迴圈器會檢查事件佇列是否為空,如果為空,繼續檢查;不為空,執行2;
- 取出事件佇列的首項,壓入執行棧;
- 執行執行棧;
- 檢查執行棧是否為空,如果為空,執行1;不為空,繼續檢查;
macro task與micro task
非同步任務被分成兩類微任務(micro task)和巨集任務(macro task);
macro task: script 指一開始執行的 <script> 標籤 setTimeout(); setInterval(); setImmediate() I/O 互動事件 micro task: new Promise(); new MutaionObserver();
同一事件迴圈中,微任務的執行優先順序高於巨集任務,只有當微任務為空後,才會去巨集任務的執行佇列中取出最前面的一個事件,壓入執行棧
注意事項
- 非同步程式是一個一個獨立的任務,這些任務包括:setTimeout、setInterval、ajax、eventListener等
- 事件佇列嚴格按照時間先後順序將任務壓入執行棧中;
- 當執行棧為空時,瀏覽器會一直不停的檢查事件佇列,如果不為空,則取出第一個任務;
- 在每一個任務結束後,瀏覽器會對頁面進行渲染;保障使用者瀏覽頁面時不會出現頁面阻塞的可能;
- 當執行棧為空時,便生成一個microtask檢查點;
- 微任務的執行優先順序高於巨集任務;
擴充套件
記憶體區域 堆(heap)和棧(stack)
Javascript執行程式碼時會將不同的變數存於記憶體中不同的位置:堆(heap)和棧(stack),堆中存放一些基礎型別的變數以及物件的指標,棧中存放著一些基礎型別的變數以及物件的指標;
每個執行緒分配一個stack,每個程序分配一個heap,stack是執行緒獨佔的,heap是執行緒共用的。stack建立時,大小確定,資料超過這個大小,會發生stack overflow錯誤;heap大小不確定,需要的話可以不斷增加;
資料存放的規則是:只要是區域性的,佔用空間確定的資料,一般放在statck,否則放在heap裡面;
stack資料在任務結束後就會銷燬; heap中的資料的銷燬依賴垃圾清理機制;一般記憶體洩漏發生在heap,即某些記憶體不再使用,卻因為種種原因,沒有被系統回收;