Javascript事件循环机制以及渲染引擎何时渲染UI

2018-02-09 12:46:19来源:segmentfault作者:skycity人点击

分享
JavaScript的一大特点就是单线程,而这个线程中拥有唯一的一个事件循环。

事件循环基本概念JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。
一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。
任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。
macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。// setTimeout中的回调函数才是进入任务队列的任务
setTimeout(function() {
console.log('xxxx');
})
// 非常多的同学对于setTimeout的理解存在偏差。所以大概说一下误解:
// setTimeout作为一个任务分发器,这个函数会立即执行,而它所要分发的任务,也就是它的第一个参数,才是延迟执行来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。
其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。事件循环执行循序

事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后,本轮循环结束。下一轮循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。


当我们在执行setTimeout任务中遇到setTimeout时,它仍然会将对应的任务分发到setTimeout队列中去,但是该任务就得等到下一轮事件循环执行。


那么整个事件循环中何时进行ui render呢?
<div>
begin
</div>
setTimeout(function() {
// 应该是这里执行前开始渲染ui,试试用alert阻塞下。
alert(' ui 已经渲染完毕了吗? ');
console.log('timeout1');
})new Promise(function(resolve) {
console.log('promise1');
for(var i = 0; i < 1000; i++) {
i == 99 && resolve();
}
console.log('promise2');
}).then(function() {
console.log('then1');
alert(' ui 开始渲染 ');
})console.log('global1');div.innerHTML = 'end';

上述代码中修改了div的内容,那么在执行那句js代码之后渲染引擎开始修改div的内容呢?


根据HTML Standard,一轮事件循环执行结束之后,下轮事件循环执行之前开始进行UI render。即:macro-task任务执行完毕,接着执行完所有的micro-task任务后,此时本轮循环结束,开始执行UI render。UI render完毕之后接着下一轮循环。


在chrome浏览器中执行以上代码,控制台先输出promise1,promise2,global1,then1(micro-task任务输出),弹出'ui 开始渲染'警告框,点击确定之后,页面中的'begin'变为'end',再弹出警告框'ui 已经渲染完毕了吗?' ,点击确认之后再输入timeout1.


参考文献:
深入核心,详解事件循环机制

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台