在node與瀏覽器中,JS的事件循環

在node與瀏覽器中,JS的事件循環(Event Loop)

今天羣裏討論一道題目,執行的結果是什麼html

console.log(1)

setTimeout(() => {console.log(2)})

Promise.resolve().then(() => {console.log(3)}).then(() => {console.log(4)})

console.log(5)

結果是 15342前端

以前只是明白JS事件循環的大概,今天想完全把它弄懂,查了一些資料,理了一下思路。node

爲何JS會有事件循環

由於Javascript是單線程運行的。爲何必須是單線程?由於在JavaScript誕生之初就是爲了在瀏覽器中運行操做DOM,若是是多線程,兩個線程同時操做一個DOM,那麼瀏覽器會灰常糾結,不知道該聽誰的。promise

單線程會有一個問題,就是順序執行,碰到耗時的任務,會一直等待下去,沒有效率。因此有了事件循環,把異步的任務添加到一個事件隊列中去,主進程繼續執行下面的代碼,等到異步任務處理完成,纔會通知主進程執行異步任務的回調,大大提升了效率。若是部署得好,JavaScript程序是不會出現堵塞的,這就是爲何NodeJS平臺能夠用不多的資源,應付大流量訪問的緣由。瀏覽器

瀏覽器中的事件循環

咱們都知道瀏覽器是多進程的(一個進程中能夠有多個線程,JavaScript是單線程的),其中有個進程對於咱們前端工程師來講是很熟悉的,那就是渲染進程,也叫瀏覽器內核。渲染進程包含了幾個進程,以下前端工程師

  • GUI渲染線程 (負責解析 HTML CSS 渲染成頁面)
  • JS引擎線程 (負責執行JS代碼,也就是咱們常說的V8引擎)
  • 事件觸發線程 (也就是我理解的觸發事件隊列的,一些異步的操做(setInternal、setTimeout、異步請求等)完成後的回調都放在這裏,等待JS引擎線程執行完代碼後就開始執行這裏面的回調函數)
  • 定時觸發器線程 (用於setInternal與setTimeout的計時,時間到了,就會把他們的回調放入事件隊列中,等待JS主進程執行完代碼空閒時執行)
  • 異步http請求線程 (就是咱們常說的異步請求,完成後,也是把它的回調放入事件隊列)

上面的代碼爲何會輸出 15342 呢,按道理來講,setTimeout函數是異步,會把它放入事件隊列,繼續執行下面代碼,應該是 13452,
這就要說一下在JS中,還有一種叫microtask的東西,這個小東西是放在主任務執行完以後,事件隊列中的回調函數以前執行。Promise就屬因而microtask多線程

也就是主任務開始執行,碰到異步任務的放入事件隊列中,繼續執行主任務,主任務執行結束,當即執行microtask,而後執行事件隊列中的回調函數。異步

NodeJS中的事件循環

node中的事件循環和瀏覽器會有不一樣,看下面代碼,輸出結果是什麼socket

setTimeout(() => console.log(1))

setImmediate(() => console.log(2))

process.nextTick(() => console.log(3))

Promise.resolve().then(() => console.log(4))

;(() => console.log(5))()

node官網上有對Event Loop介紹ide

clipboard.png

這張圖中,node中事件循環包括六個階段,當主任務執行完以後,會依次進入這六個階段

  • timers (處理setTimeout和setInterval的回調函數,主線程會檢查一下當前時間,是否知足定時器的條件。若是知足就執行回調函數,不然就離開這個階段。)
  • I/O callbacks (執行一些錯誤的回調和上輪應該在poll執行而沒有執行的回調)
  • idle prepare (這個階段只供 libuv 內部調用,咱們能夠先忽略)
  • Poll (等待還未返回的 I/O 事件,並執行回調,等待的過程當中若是有timer到時間,那就進入timer階段,若是有setImmediate,直接進入check階段)
  • check (該階段執行setImmediate()的回調函數)
  • close callbacks (該階段執行關閉請求的回調函數,好比socket.on('close', ...))

上面代碼 setTimeout放入事件循環隊列中(雖然沒有設置延遲的時間,可是它是異步函數,仍是要放入事件循環隊列中),接着 setImmediate 也是要放入事件循環隊列,接着 process.nextTick (它y優先於promise執行,在主任務執行完當即會執行它),接着 Promise (不用多說了,可是會在 process.nextTick 以後執行,優先級不如process.nextTick高),接着是當即執行的同步函數,打印 5,緊接着執行 process.nextTick ,打印 3 ,緊接着執行 Promise,打印 4。

接下來就進入事件循環了,首先進入 timers 階段,檢查後發現已經到時間,執行回調,打印 1 ,而後是執行下面的階段,等到了check 階段,執行 setImmediate 的回調,打印 3 。

這只是一種狀況,還有不少種狀況能夠自行試一試。這樣就會對node的事件循環機制有更深的瞭解。

初步的認識,若有錯誤,還請指正~

參考文章

https://funteas.com/topic/5a64e9482630e6f31583701d
http://www.ruanyifeng.com/blog/2018/02/node-event-loop.html
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

相關文章
相關標籤/搜索