日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

彻底理解 JS Event Loop(浏览器环境)

發布時間:2023/12/29 javascript 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 彻底理解 JS Event Loop(浏览器环境) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近羅列了一些軟件開發基礎知識點,計劃逐一的、徹底的理解每一個知識點,并為每個知識點寫一篇詳細的,圖文并茂的文章。這篇是關于瀏覽器環境下 JS 的 Event Loop 機制(如有錯誤,歡迎指出)。

瀏覽器線程

我們常說 JS 是單線程語言,但是別忘了常見的瀏覽器內核可都是多線程的,多個線程間會進行不斷通訊,通常會有如下幾個線程:

  • GUI 渲染進程
  • JS 引擎線程
  • 定時器線程
  • 事件觸發線程
  • 異步 HTTP 請求線程

Microtask 與 Macrotask

在大多數解釋 JS Event Loop 的文章中,鮮有談及 Miscrotask 和 Macrotask 這兩個概念,但這兩個概念卻是非常的重要,我在翻閱 Zone.js Primer 時,里面就經常會提及這兩個概念,當時也是看的云里霧里的,這也是我寫這篇文章的原因之一。

setTimeout(function () {console.log('timeout1'); }, 0);console.log('start');Promise.resolve().then(function () {console.log('promise1');Promise.resolve().then(function () {console.log('promise2');});setTimeout(function () {Promise.resolve().then(function () {console.log('promise3');});console.log('timeout2')}, 0); });console.log('done'); 復制代碼

以上代碼最后會輸出什么呢?如果你能很快的回答出來,你大概就已經掌握了 Event Loop 的實際運用了,如果回答不出,那可能還得接著往下看。

問題:是先執行 then( ) 中的回調函數呢,還是 setTimeout( ) 中的回調函數呢?

答案:先執行前者。因為 Promise.prototype.then( ) 是 Microtask ,而 setTimeout( ) 是 Macrotask 。至于為什么先執行 Miscrotask ?繼續往后看~

在 JS 線程中程序的每一個調用都被看成是一個任務(task) ,所有的任務被分成許多類型且存放在對應類型的隊列中,為了方便理解,我把這些任務隊列分成三類:

  • Micro-task queue: 存放 microtask 的回調函數。

  • Macro-task queue: 存放 macrotask 的回調函數 。

  • Other-task queue: 這是一個我個人抽象出來隊列,實際并不存在,假設該隊列用來存放除了 microtask 和 macrotask 外的所有任務。

Microtask 和 Macrotask 的區別就是執行順序上的區別。簡單的說,JS 線程會先處理 other-task queue 上的任務,處理完了之后,再去處理 micro-task queue 上的任務,最后才處理 macro-task queue 上的任務。至于 JS 線程具體的執行細節,后面會詳細的進行描述。

以下是常見的 Microtask 和 Macrotask:

  • Microtask :Promise.prototype.then( )、MutationObserver.prototype.observe( ) 等 。

  • Macrotask :setTimeout( )、setImmediate( )、XMLHttpRequest.prototype.onload( ) 等。

JS 線程 Event Loop 的實現

如上,根據個人的理解,我畫了一個瀏覽器環境下 JS 實現 Event Loop 大致模型圖,具體含義如下:

1 獲取執行的任務,執行步驟 1.1

1.1 判斷 other-task queue 中是否有任務,如果有,獲取最早的任務然后執行步驟 2 ,否則執行步驟 1.2 。

1.2 判斷 micro-task queue 中是否有任務,如果有,獲取最早的任務然后執行步驟 2 ,否則執行步驟 1.3 。

1.3 判斷 macro-task queue 中是否有任務,如果有,獲取最早的任務然后任何執行步驟 2 ,否則執行步驟 3 。

2 將取到的任務放到 call stack 并執行,執行完之后再執行步驟 1 (值得注意的是,在執行的過程中,是會不斷的更新所有的 task queue ,因為 call stack 中正在執行的任務內部也可能存在普通任務、microtask 和 macrotask ,執行任務的過程可以理解為一個遞歸過程,如果無限遞歸,call stack 上待執行的任務就會不斷累積而溢出,這也就是常見的 Maximum call stack size exceeded 錯誤)。

3 線程會處理其他工作,例如:不斷同步「事件觸發線程」的狀態,一旦有事件觸發,即查看觸發事件「target」有沒有對應事件的監聽器任務,如果有,則選中該任務并執行步驟 2 。需要注意的是,并不是只有執行了步驟 1.3 后才會執行當前步驟,JS 線程肯定還會在的某個時候去同步其他線程的狀態的。

接下來,如果仔細想,可能會產生一個疑問:JS 進程是如何更新 micro-task queue 和 macro-task queue 這兩個隊列的呢 ?

根據我的理解,micro-task queue 和 other-task queue 都是“同步”更新的,而 macro-task queue 是“異步”更新。以下是 macro-task queue 更新的具體流程(以 setTimeout 為例):

  • JS 線程判斷某個 macrotask 是一個定時器,將這個定時器同步給定時器線程。
  • 定時器線程啟動從 JS 線程收到的定時器。
  • JS 線程在某個時候(可能是執行上述步驟 1 的時候)會通過定時器和 http 請求等一些線程來更新 macro-task queue ,即如果以上的定時結束了,JS 線程就可以將對應定時器的回調函數存放到 macro-task queue 中。
  • 如何理解 JS 中的異步

    目前普遍對異步的解釋可能是:執行調用,如果立即得到結果就是同步調用,否則為異步調用。

    在 JS 環境中,我個人其實是不同意這個解釋的。

    首先,根據以上的解釋,setTimeout( )、Promise.prototype.then( ) 、http 請求和各類瀏覽器事件,這些都被認為是異步的。但我卻不這么認為,我認為瀏覽器事件不是異步的。以下代碼便是理由:

    // html: <button id="btn">click</button>// js var btn = document.getElementById('btn');setTimeout(function () {console.log('timeout') }, 0);Promise.resolve().then(function () {console.log('promise'); });btn.addEventListener('click', function () {console.log('click'); });btn.click();console.log('done'); 復制代碼

    如果瀏覽器事件是異步的,不管后續會打印出什么,第一個打印的必然是 done ,而實際的打印結果為:click done promise timeout 。

    也就是說,JS 認為瀏覽器事件并非異步。

    由此,我個人對異步的解釋是:在滿足調用所需的外在條件的情況下,執行調用,立即獲得結果的就為同步調用,否則為異步調用。

    根據這個理解,當我們發起的一個 http 請求時,假設服務器以光速返回請求結果,XMLHttpRequest 對象的 onload 方法會立即執行嗎?,顯然不會,所以 http 請求為異步調用。這也是為什么我在以上分析 Event Loop 中的任務隊列時并沒有將 event-task queue 拎出來的原因。因此,對于異步調用的判斷可以是這樣:如果某個調用屬于 microtask 或是 macrotask 中的其中一個,那么這個調用就是異步調用。

    題外話

    有人可能會注意到,這篇文章經常出現「我認為」和「我理解」,這并非是我對自己不自信,而是我想表達一個看法:在翻閱別人的技術文章的時候,務必保持獨立思考的能力,就算文章的作者是業界有名的大牛,也不能沒緣由的「深信不疑」,對對應的技術點務必在自個腦中里建立一個可以自圓其說的模型。至于我為什么會表達這個看法,是因為我找翻閱大量的過程中,發現大多數關于 JS Event Loop 的文章或多或少都有一些粗糙或是錯誤,如果我只看其中的某一篇,我很大的概率會有建立一個錯誤的 Event Loop 模型。當然,就我當前的理解,還是可能會有些許錯誤。Anyway ,還是那句話:保持獨立思考,與各位共勉。

    Done.?

    參考鏈接

    • Philip Roberts: What the heck is the event loop anyway? | JSConf EU 2014
    • Tasks, microtasks, queues and schedules
    • HTLM5 EVENT LOOP DEFINITIONS

    總結

    以上是生活随笔為你收集整理的彻底理解 JS Event Loop(浏览器环境)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。