本意是想好好研究下 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)