javascript事件循環

EventLoop

以下內容僅限于自己理解,可能并不全面或者有錯誤

參考文檔:https://www.cnblogs.com/jymz/p/7900439.html

JavaScript的一大特點就是單線程,而這個線程中擁有唯一的一個事件循環

依靠函數調用棧來搞定函數的執行順序外,還依靠任務隊列(task queue)來搞定另外一些代碼的執行。

函數調用棧遵循的是先進后出的原則;

1? 一個線程中,事件循環是唯一的,但是任務隊列可以擁有多個。

2 任務隊列又分為macro-task(宏任務)與micro-task(微任務),在最新標準中,它們被分別稱為task與jobs。

3 macro-task大概包括:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, XHR回調,事件回調(鼠標鍵盤事件)。

4 micro-task大概包括:process.nextTick, Promise.then(), MutationObserver(html5新特性)

5 setTimeout/Promise等我們稱之為任務源。而進入任務隊列的是他們指定的具體執行任務。

6 來自不同任務源的任務會進入到不同的任務隊列。其中setTimeout與setInterval是同源的。

7 事件循環的順序,決定了JavaScript代碼的執行順序。它從script(整體代碼)開始第一次循環。之后全局上下文進入函數調用棧。直到調用棧清空(只剩全局),然后執行所有的micro-task。當所有可執行的micro-task執行完畢之后。循環再次從macro-task開始,找到其中一個任務隊列執行完畢,然后再執行所有的micro-task,這樣一直循環下去。

8 其中每一個任務的執行,無論是macro-task還是micro-task,都是借助函數調用棧來完成。

看下面的案例

console.log('start')

setTimeout(function() {?

????console.log(timeout1)

?},0)

new Promise(function (resolve) {

? ?console.log('promise1')

? ?for (var i = 0; i <1000; i++) {

? ? ? ? i == 99 && resolve()

? ?}

}).then(function() {

? ?console.log('then1')

?})

?console.log('global')

具體的執行過程:

首先,事件循環從宏任務開始,從script開始,不同的任務源的任務進入到不同的事件隊列;

宏任務timeout1進入setTimeout隊列

script執行時遇到Promise實例。Promise構造函數中的第一個參數,是在new的時候執行,因此不會進入任何其他的隊列,而是直接在當前任務直接執行了,

如下圖


當promise 中的resolve執行完畢,出調用棧后,后續的.then會被分發到micro-task的Promise隊列中去;具體如下圖


script任務繼續往下執行,最后只有一句輸出了globa1,然后,全局任務就執行完畢了。

第一個宏任務script 執行完畢后,則開始執行微任務隊列中的任務;then1被輸出;則第一輪事件循環執行完畢。如下圖,script隊列為空,微任務隊列為空


開始執行第二輪事件循環,第二輪也是從宏任務開始,執行settimeout隊列中的任務;

需要注意的是如果settimeou任務中遇到settimeout,任務也會放到settimeout隊列,但是要到下一輪事件循環中;

setTimeout(function() {

??? ????console.log('timeout_1');

??? ????setTimeout(function() {

????? ????????console.log('timeout_2');

??? ????},0)

? },0);

? setTimeout(function() {

??? ????console.log('timeout_3');

? },0) // 執行結果為 timeout_1?timeout_2 timeout_3

每一輪事件循環會先執行宏任務,宏任務執行完了立刻執行所產生的微任務;比如在settimeout中有promise,則在settimeout中的同步代碼被執行完之后立即執行.then()中的代碼;

3/不同的任務源的執行順序

宏任務:

settimeout隊列 要比setImmediate隊列先執行;

如果settimeout和setImmediate兩者都有,則執行完settimeout隊列任務以及其所產生的微任務隊列,清空所有微任務隊列,之后再執行setImmediate任務隊列及其產生的的微任務隊列。

微任務:

process.nextTick()要比promise的.then()先執行;

如果一個宏任務產生了process.nextTick()和promise的.then()兩個微任務隊列,則process.nextTick()隊列任務執行清空之后,再執行promise的.then()

XHR回調會在數據響應回來插入到事件隊列中,而事件回調則會在被觸發時插入到事件隊列中;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 在上一篇文章里面我大致介紹了JavaScript的事件循環機制,但是最后還留下了一段代碼和幾個問題。那就從這段代碼...
    fangdown閱讀 538評論 0 1
  • 通過學習JavaScript,我們都知道它是一門單線程語言,也就是說,在同一時刻,最多也只有一個代碼段在執行,但一...
    肆意咯咯咯閱讀 547評論 0 3
  • 前端開發的童鞋應該都知道,JavaScript 是一門單線程的腳本語言。這就意味著 JavaScript 代碼在執...
    淘淘笙悅閱讀 585評論 1 2
  • 靜下心學了一波事件循環機制,好開心,我學會了,首先還是得感謝作者寫的筆記特別詳細 鏈接: http://www.c...
    Dianaou閱讀 551評論 0 0
  • 葵花籽 嗑葵花籽,吃的有點多以后,胸口氣機不太通利了,稍微有點悶悶的。瓜子仁,精華收藏的比較足,稍微有點偏黏的感覺...
    中醫李奇飛閱讀 745評論 0 1