事件循环
主线程
这里主要做的事情是JS执行、DOM渲染。这意味着网页发生的事情是按照某个顺序执行的,不会发生竞争。
关键点GUI渲染线程与JS引擎线程是互斥的。因此当JS线程某个事件导致发生死循环时,会导致渲染阻塞,其他的事件也会阻塞。
关键点其他的线程会通过事件,触发主线程的回调函数。
事件环
关键点渲染可能发生在两个任务之间,也可能两次渲染之间有在多个任务
JS线程先执行结束,才会发生渲染。如果发生DOM变化,渲染线程才会接下来执行。因此下面代码不会发生闪烁。
document.body.appendChild(el);
el.style.display = 'none';
下面的代码不会发生渲染阻塞,因为JS代码有执行结束,只不过计时器函数放到任务队列等待执行。
function loop(){
setTimeout(loop, 100);
}
loop();
requestAnimationFrame(rAF回调队列)
关键点会在下一帧的开始执行,直到队列为空,然后执行渲染。
function callback(){
moveBoxOnePixel(); //盒子移动1px
setTiomeout(callback, 0); //因为显示器的刷新频率是60HZ,因此如果使用计时器不断的渲染页面,其实是无意义的损耗性能。渲染数据准备好了但是显示屏不会立即刷新,用户也看不到。
requestAnimationFrame(callback)
}
callback();
当你准备更新动画时你应该调用此方法。这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数(即你的回调函数)。回调函数执行次数通常是每秒60次,但在大多数遵循W3C建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。
使用setTimeout渲染动画的缺点。每一帧动画的渲染时间无法确定,因为当有其他的事件需要执行时,导致动画推迟渲染。
关键点由浏览器决定什么时候渲染动画,而不是在JS代码执行一结束,就执行渲染。不会因任务的出现,影响下个时间段的页面渲染。
宏任务
```javascript //DOMNodeInserted事件监听DOM的变化。 document.body.addEventListener(‘DOMNodeInserted’, () => { console.log(1); })
for(var i = 0; i < 5; i++ ){
const div = document.createElement(‘div’);
document.body.appendChild(div);
}
```
在事件循环中,一旦监听到事件发生,就会加入到队列中执行,每次循环执行一次,同时会发生一次渲染。对于批量的DOM操作,这是明显消耗性能的。我们希望浏览器多个事件触发后,等一会儿再执行渲染。
关键点宏任务执行一次,然后渲染一次,不断循环。
微任务
UI渲染需要等到全部微任务都执行完,再执行渲染。有点类似与文档片段,检测到全部的DOM变化之后,在执行渲染。
关键点在一次事件循环中微任务会一直执行,直到队列为空,然后再执行渲染。