開發者的javascript造詣取決於對【動態】和【異步】這兩個詞的理解水平。javascript
題目是這樣的,要求寫出下面代碼的輸出:java
setTimeout(() => { console.log(1) }, 0) new Promise((resolve, reject) => { console.log(2) for (let i = 0; i < 10000; i++) { i === 9999 && resolve() } console.log(3) }).then(() => { console.log(4) }) console.log(5)
若是沒有詳細鑽研過異步隊列,答對的可能性很低。題目的考察點很明確,就是javascript
中最核心的特色之一的【異步】,瞭解了原理之後,你就會明白javascript
中聲稱的「無阻塞」並非徹底成立的,經過一些小辦法就可讓setTimeout( )
的回調永遠都沒法被執行,儘管這看起來除了知足整蠱需求之外並無什麼明顯的實用價值。node
對Event Loop的理解,帶給開發者的是對代碼整個生命週期更精細的控制能力,儘管在依賴於SPA框架的開發中你幾乎不會用到它們。git
(上圖來自下面推薦的這篇博文)github
【極力推薦文章】:面試
https://github.com/nswbmw/node-in-debugging/blob/master/3.6%20Event%20Loop.md瀏覽器
並非筆者偷懶不想寫這一節,而是在讀過了這篇教程之後,自認爲除非是剖析更底層的libuv
的原理,不然僅就理解Event Loop而言,筆者本身認爲不會比這篇寫的清晰。框架
上文中給出了從簡單到複雜共6道題來供讀者自檢,算是很是貼心了,本文中針對最後一題進行一些講解。你會發現只要理解了Event Loop 的基本原理後,分析這類代碼基本就是一個【完形填空】的過程。異步
題目以下:函數
setImmediate(() => { console.log(1) setTimeout(() => { console.log(2) }, 100) setImmediate(() => { console.log(3) }) process.nextTick(() => { console.log(4) }) }) process.nextTick(() => { console.log(5) setTimeout(() => { console.log(6) }, 100) setImmediate(() => { console.log(7) }) process.nextTick(() => { console.log(8) }) }) console.log(9)
題目分析:
爲了方便分析,先作代碼分塊:
將代碼塊放入事件循環:
分析:
這裏有必要說明一下Fn2
的位置,文中並無明確說起同步代碼執行完畢後進入異步隊列時會先經歷Tick
階段,就圖示而言,每個宏觀任務階段之間都會檢查Tick
隊列(你也能夠理解爲每次函數的調用棧被清空的時候會檢查一次Tick
隊列),那麼Fn2
的待執行時序也就很好理解了。爲了方便分析,將console.log(n)
相關的方法稱爲cln
。
接下來看一下當執行至Fn2
時發生的事情:
分析:
Tick
隊列中的process.nextTick( )
回調會直接加入Tick
隊列(此處就能夠實現篇頭講到的阻塞事件循環)。另外講一下CL6
這個回調,它上面綁定了一個100ms的定時器,在後續的Timers
和IO Polling
中都會檢查倒計時是否到期,到了就執行,沒到就等下一次Timers
或IO Polling
階段再檢查。從上例來看,推遲100ms的CL6
在沒有其餘干擾的狀況下幾乎必定會在N個event loop之後才被執行。
一樣的道理來拆分一下Fn1
:
分析:
CL6
比CL2
先開始計時,因此倒計時100ms先到,固然這是N個事件循環之後的事情了。
因此從上面的時序就能夠看到輸出的結果:9 5 8 1 7 4 3 6 2
【思考題】:
外加一個思考題,若是上例中CL6
和CL2
的延遲都是0,結果是怎樣的呢?
requestAnimationFrame()
不少時候會被拿來和setTimeout()
做對比,這個API是瀏覽器環境下爲了實現高性能幀動畫而設計的,它的主要目的是爲了讓瀏覽器的重繪可以配合顯示設備的刷新率而去掉沒必要要的性能開銷,常見的顯示設備刷新率爲60Hz
,至關於你每1000/60≈16.7ms只能看屏幕一眼,獲得的信息都是依靠這些離散畫面的視覺暫留拼湊起來的,那若是動畫元素在你看屏幕的時間間隔中像素位移過大的話,看起來就會是一卡一卡的,也就是平時常說的「丟幀」,從Event Loop的角度來說的話,將其近似理解爲setTimeout(fn, 1000/刷新率)
就能夠了。
編輯/尋水的魚
本文首發於華爲雲社區:原文連接