首页 > 其他 > 详细

前端面试-eventloop事件循环篇

时间:2021-05-06 15:20:07      阅读:26      评论:0      收藏:0      [点我收藏+]
以下是对微信公众号【前端大全】的文章【一位摸金校尉决定转行前端】的总结
 
一般浏览器的刷新率为60HZ,即1秒钟刷新60次
技术分享图片

 

 

其中task被称为宏任务,包括setTimeout,setInterval,DOM 事件,I/O,setImmediate(网上据说node常用,chrome不支持,IE支持,用于立即运行),postMessage,requestAnimationFrame,等。
render指渲染页面。
 
技术分享图片

 

 

虚线框部分要做的工作是:
将新产生的task插入不同task queue中。
按优先级从某个task queue中选择一个task作为本次要执行的task。这就是事件循环(eventLoop)。
 
task执行过程中如果调用Promise、MutationObserver、process.nextTick会将其作为microTask(微任务)保存在microTask queue中。
每当执行完task,在执行下一个task前,都需要检查microTask queue,执行并清空里面的microTask。
技术分享图片 
 
setTimeout(() => console.log(‘timeout‘));
Promise.resolve().then(() => {
    console.log(‘promise1‘);
    Promise.resolve().then(() => console.log(‘Promise2‘));
});
console.log(‘global‘);
全局代码就是第一个task
调用计时器线程,计时结束后将setTimeout回调入task
promise入microTask
打印“global”
执行完全局task,执行microTask,打印‘promise1‘(Promise的回调是在当前轮循环中产生,并推入到当前循环的微任务队列)
其中又遇Promise,入microTask,继续遍历microTask queue,执行并打印“Promise2”,第一次全局task执行完毕
开始第二个task,打印timeout
 
在一帧时间可以执行多个task
屏幕会先显示红色再显示黑色,还是直接显示黑色?
document.body.style.background = ‘red‘;
setTimeout(function () {
    document.body.style.background = ‘black‘;
})
全局代码执行和setTimeout为不同的2个task。
如果这2个task在同一帧中执行,则页面渲染一次,直接显示黑色
如果这2个task被分在不同帧中执行,则每一帧页面会渲染一次,屏幕会先显示红色再显示黑色
如果我们将setTimeout的延迟时间增大到17ms,那么基本可以确定这2个task会在不同帧执行,则“屏幕会先显示红色再显示黑色”的概率会大很多
 
使用requestAnimationFrame(简称rAF),rAF会在每一帧render前被调用
setTimeout(() => {
  console.log("setTimeout1");
  requestAnimationFrame(() => console.log("rAF1"));
})
setTimeout(() => {
  console.log("setTimeout2");
  requestAnimationFrame(() => console.log("rAF2"));
})
Promise.resolve().then(() => console.log(‘promise1‘));
console.log(‘global‘);
第一个setTimeout计时完成后回调将放入task,第二个则是4ms后放入task
promise入micro task
打印“global”
执行完全局task后,清空micro task,打印“promise1”
由于两个setTimeout回调间隔4ms,大概率在同一帧执行
打印“setTimeout1”,执行rAF1第一次大概率在本帧执行却是下一个task执行
打印“setTimeout2”,因为几乎和settimout1同一时间执行,紧跟在settimout1之后
打印rAF1,在下一帧打印rAF2(因为本贞已经调了一次requestAnimationFrame)
 
使用requestIdleCallbackAPI,如果渲染完成后还有空闲时间,则这个API会被调用
task执行时间超过了16.6ms则会掉帧,页面直到下一帧render后才会更新
React15中,采用递归的方式构建虚拟DOM树。如果树层级很深,对应task的执行时间很长,就可能出现掉帧的情况
为了解决掉帧造成的卡顿,React16将递归的构建方式改为可中断的遍历。
以5ms的执行时间划分task,每遍历完一个节点,就检查当前task是否已经执行了5ms。
如果超过5ms,则中断本次task。
技术分享图片

 

  

通过将task执行时间切分为一个个小段,减少长时间task造成无法render的情况。这就是时间切片
 
 
这其中大家需要了解几个概念:调用栈、同步/异步任务、任务队列、宏任务/微任务
JavaScript本身是单线程,也就是同一时刻只能干一件事
遇到执行函数会将其放入调用栈(先进后出)中
遇到setTimeout/setInterval等异步任务时,会把它放入到消息队列中
等主线程的任务执行完成以后,再回过头执行消息队列中的异步任务
如果异步任务中仍然有异步任务,会继续放入消息队列
以此类推,便形成了一个事件循环。
 
技术分享图片

 

 

也就是说包含await fn()的函数会让当前停止,先执行fn(),然后await的下一行代码是个promise resolve,放入微任务队列
 
 
 
 

前端面试-eventloop事件循环篇

原文:https://www.cnblogs.com/vinfy2018/p/14734246.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!