穿越了整個沙漠,只爲尋eventloop的真相

今天主要講一下eventloop在瀏覽器中與在node中運行的機制,首先,不得不來講一下,下面幾個名詞的概念。javascript

Node是什麼?

  • Node.js是一個基於 ChromeV8引擎的JavaScript運行環境(runtime),Node不是一門語言,而是讓js運,行在後端的運行時,而且不包括javascript全集,由於在服務端中不包含DOM和BOM,Node也提供了一些新的模塊例如http,fs模塊等。css

  • Node.js 使用了事件驅動、非阻塞式 I/O 的模型,使其輕量又高效而且Node.js 的包管理器 npm,是全球最大的開源庫生態系統。java

Node的優點

  • 高併發,是指在同一時間併發訪問服務器node

  • I/O密集指的是文件操做、網絡操做、數據庫,相對的有CPU密集,CPU密集指的是邏輯處理運算、壓縮、解壓、加密、解密ajax

進程與線程

談談瀏覽器

  • 用戶界面-包括地址欄、前進/後退按鈕、書籤菜單等數據庫

  • 瀏覽器引擎-在用戶界面和呈現引擎之間傳送指令(瀏覽器的主進程)npm

  • 渲染引擎,也被稱爲瀏覽器內核(瀏覽器渲染進程)後端

  • 一個插件對應一個進程(第三方插件進程)promise

  • GPU提升網頁瀏覽的體驗(GPU進程)瀏覽器

渲染引擎

  • 渲染引擎內部是多線程的,內部包含兩個最爲重要的線程ui線程和js線程。

  • 這裏要特別注意ui線程和js線程是互斥的,由於JS運行結果會影響到ui線程的結果。

  • ui更新會被保存在隊列中等到js線程空閒時當即被執行。

js單線程

  • javascript在最初設計時設計成了單線程,爲何不是多線程呢?

  • 若是多個線程同時操做DOM那豈不會很混亂?這裏所謂的單線程指的是主線程是單線程的,因此在Node中主線程依舊是單線程的。

  • js只能是單線程的 不能兩個線程同時操做一個dom

  • ui進程與js進程是互斥的,不能同時執行,執行js後空閒下來了,再去執行css

其餘線程

  • 瀏覽器事件觸發線程(用來控制事件循環,存放setTimeout、瀏覽器事件、ajax的回調函數)

  • 定時觸發器線程(setTimeout定時器所在線程)

  • 異步HTTP請求線程(ajax請求線程)

隊列&棧

  • 隊列遵循:先進先出;

以下圖所示:

  • 棧遵循:先進後出;

下列代碼中,函數是放到棧中的,看看它的執行順序

function fn1() {
    let a = 1;
    fn2();
    function fn2() {
        console.log(a);
        let b = 2;
        function fn3() {
            debugger;
            console.log(b);
        }
        fn3();
    }
}
fn1();
複製代碼

執行結果以下圖所示:

宏任務&微任務

  • 常見的(macro-task)宏任務:setTimeout setImmediate(只兼容ie) MessageChannel

  • 常見的(micro-task)微任務:promise.then (MutationObserver);

瞭解了以上幾個名詞的概念以後,下面重點來了,先看一下EventLoop在瀏覽器中的運行機制

瀏覽器中的EventLoop

  • 先會執行棧中的內容,棧中內容執行後執行微任務,微任務清空後再執行宏任務,宏任務會在棧中執行,不停的循環;
Promise.resolve('1').then(data => { console.log('第一個promise') });
setTimeout(() => {
    console.log('setTimeout1')
    Promise.resolve('2').then(data => { console.log('第二個promise') });
});
setTimeout(() => {
    console.log('setTimeout2');
});
複製代碼

代碼運行的結果爲:

第一個promise
setTimeout1
第二個promise
setTimeout2
複製代碼
1,setTimeout有一個4ms的最短期,也就是說無論你設定多少,反正最少都要間隔4ms才運行裏面的回調,因此先執行了第一個promise異步回調,輸出了第一個promise
2,setTimeout到運行時間了,輸出了setTimeout1,
3,依次輸出了第二個promise(Promise的任務會在當前事件循環末尾中執行)
4,最後,第二個setTimeout到時間了,輸出了setTimeout2
複製代碼

EventLoop在node中的運行機制

Node中的EventLoop

  • nextTick(微任務)是隊列切換時執行的

  • setTimeout和setImmediate順序是不固定,看node準備時間

setImmediate(() => {
  console.log('setImmediate1')
  setTimeout(() => {
    console.log('setTimeout1')
  }, 0);
})
setTimeout(()=>{
  process.nextTick(()=>console.log('nextTick'))
  console.log('setTimeout2')
  setImmediate(()=>{
    console.log('setImmediate2')
  })
},0);
複製代碼

第一種狀況,若是先執行了setImmediate函數,那麼它的執行順序爲:

setImmediate1
setTimeout2
setTimeout1
nextTick
setImmediate2
複製代碼
1,根據上圖所示,若是先執行了setImmediate回調,把setImmediate1輸出了,
2,接下來,會執行setTimeout回調,輸出setTimeout2,
3,接着輸出setTimeout1,接着微任務nextTick,
4,最後,輸出setImmediate2
複製代碼

第二種狀況,若是先執行了setTimeout函數,那麼它的執行順序爲:

setTimeout2
nextTick
setImmediate1
setImmediate2
setTimeout1
複製代碼
1,根據上圖所示,若是先執行了setTimeout回調,把setTimeout2輸出了,
2,接下來,會執行poll輪詢,那就是輸出了nextTick,
3,接下來執行check隊列中setImmediate函數,把setImmediate1輸出了,check隊列裏的內容得先執行完,才能切換下一個隊列,因此輸出了setImmediate2,
4,最後循環到了setTimeout回調,輸出了setTimeout1
複製代碼

結語:

關於eventloop在不一樣環境中運行的機制,首先要對上述提到的,隊列&棧、微任務&宏任務要有深刻的理解,但願這篇內容對你們有所幫助,固然也但願你們若是有什麼問題,能夠隨時加關注,進行討論!

相關文章
相關標籤/搜索