本意是想好好研究下 Event Loops, 看了幾篇博客後,才意識到做爲前端打字員的我有多無知,這坑忒深了。javascript
macrotask?,microtask?,MutationObserver? 這些都是啥?規範還沒寫清楚?不一樣瀏覽器運行的還未必同樣?html
但爲了使本身養成常常總結的習慣,仍是寫點什麼吧。前端
<body> <script> const fib = n => { if (n <= 1) { return 1 } else { return fib(n - 1) + fib(n - 2) } } console.now = (text) => { console.log(`${text} ${new Date().toLocaleString()}`) } setTimeout(function () { console.now(0) document.getElementsByTagName('body')[0].style.backgroundColor = 'red' console.now(1) fib(45) console.now(2) }, 1000) </script> </body>
計算 fib(45)
是一個至關耗時的工做,在個人chrome里約須要15s左右。html5
問,頁面何時會變成紅色?在執行 console.now(1)
以前就變成紅色了嗎?java
能夠看到即便在 console.now(1)
執行以後,頁面仍舊沒有變紅。git
關於這個現象,能夠有兩種解釋:github
document.getElementsByTagName('body')[0].style.backgroundColor = 'red'
被看成一個異步事件,做爲一個 task,被添加到 event loops究竟是哪種?因此將上述代碼修改下web
<script> const fib = n => { if (n <= 1) { return 1 } else { return fib(n - 1) + fib(n - 2) } } console.now = (text) => { console.log(`${text} ${new Date().toLocaleString()}`) } setTimeout(function () { console.now(0) document.getElementsByTagName('body')[0].style.backgroundColor = 'red' console.now(1) fib(45) console.now(2) }, 1000) setTimeout(function () { console.now(3) fib(45) console.now(4) }, 1000) </script>
又增長了一個 setTimeout
。這樣的話,若是是第一種解釋,應該在 console.now(3)
運行以前,頁面就變成了紅色;不然就應該採起第二種解釋。chrome
運行結果以下,api
能夠看到在 console.now(3)
以後,頁面依舊沒有變色,看來就是渲染引擎要等到JS引擎徹底空閒時才工做。
沒有,直到我看到文檔
An event loop must continually run through the following steps for as long as it exists:
- Let oldestTask be the oldest task on one of the event loop's task queues, if any, ignoring, in the case of a browsing context event loop, tasks whose associated
Document
s are not fully active. The user agent may pick any task queue. If there is no task to select, then jump to the microtasks step below.- Set the event loop's currently running task to oldestTask.
- Run oldestTask.
- Set the event loop's currently running task back to null.
- Remove oldestTask from its task queue.
- Microtasks: Perform a microtask checkpoint.
- Update the rendering: If this event loop is a browsing context event loop (as opposed to a worker event loop), then run the following substeps.
…...
這段話第7點的意思,怎麼理解起來像是每執行一次 Event Loops 的 task,最後都會更新視圖。
後來看到從event loop規範探究javaScript異步及瀏覽器更新渲染時機中
渲染更新(Update the rendering)會在event loop中的tasks和microtasks完成後進行,但並非每輪event loop都會更新渲染,這取決因而否修改了dom和瀏覽器以爲是否有必要在此時當即將新狀態呈現給用戶。
會不會兩次 setTimeout 被合併了?
<script> const fib = n => { if (n <= 1) { return 1 } else { return fib(n - 1) + fib(n - 2) } } console.now = (text) => { console.log(`${text} ${new Date().toLocaleString()}`) } setTimeout(function setTimeout0() { console.now(0) document.body.style.backgroundColor = 'red' console.now(1) fib(45) console.now(2) }, 1000) setTimeout(function setTimeout1() { console.now(3) fib(45) console.now(4) }, 1001) </script>
這樣調整以後,在運行 console.now(3)
以前,頁面的顏色就變了
這樣看來,就是在每一次task以後就可能會更新視圖,而不是等到JS引擎空閒
在執行完setTimeout0
後,Event Loops 中實際上仍有 setTimeout1
待執行,可是瀏覽器先渲染了視圖,再執行了setTimeout
,這就推翻了以前渲染引擎要等到 JS 引擎空閒(Event Loops爲空)時纔開始工做。
同時我懷疑,以前代碼
setTimeout(function () { console.now(0) document.getElementsByTagName('body')[0].style.backgroundColor = 'red' console.now(1) fib(45) console.now(2) }, 1000) setTimeout(function () { console.now(3) fib(45) console.now(4) }, 1000)
會不會被優化成
setTimeout(function () { console.now(0) document.getElementsByTagName('body')[0].style.backgroundColor = 'red' console.now(1) fib(45) console.now(2) console.now(3) fib(45) console.now(4) }, 1000)