发布网友 发布时间:2024-09-17 05:03
共1个回答
热心网友 时间:2024-10-02 10:06
WhyJavascript是一门单线程语言,也就是同一时间只能执行一个任务。在浏览器环境中存在很多异步任务,比如Promise、事件回调、setTimeout等这些异步任务。这些任务执行时间是不定的,满足一定的条件后就会调用这些异步回调函数。所以就需要一套机制来管理这些异步任务,不至于页面的阻塞。也就是Eventloop机制
简介其实eventloop就是一个不断循环的读取异步任务的机制,首先他有一套既定的规则:
首先检查同步任务有没有执行完(也就是调用栈是否为空)。
如果为空的话,就去检查microtask(微任务)队列是否有任务,如果有的话就一次性把microtask(微任务)执行完为止(包括microtask(微任务)执行过程中再次生成微任务),直到microtask(微任务)队列为空再去检查宏任务macrotask(宏任务)队列。每执行完一个macrotask(宏任务)还要去检查microtask(微任务)队列是否为空,不为空的话先去把microtask队列清空。如此反复。
任务上面说了microtask与macrotask就是异步任务的分类。创建它们的API也是不一样的比如:
创建microtask(微任务)的有Promise、MutationObserver等
创建macrotask(宏任务)的有:setTimeout、setInterval、requestAnimationFrameMessageChannel还有一些IO事件等(这里rAF有争议,但它绝对不是微任务)
任务表现差异任务都是在调用栈中执行的,不断什么任务,单个任务是不能被中断的。
关于microtask(微任务)与macrotask(宏任务)上面说了它们的执行顺序不一样,也就是微任务的优先级高。但是还有一个最重要的不同任务表现区别,也就是这篇文章最想要记录的—微任务会阻塞页面的渲染,给用户造成卡顿的感觉。EventLoop类似的规则如下:
可以看出直到把microtask(微任务)队列情况才去执行渲染,有关视频可以去事件循环的进一步探索-ErinZimmer-JSConfEU2018看
我也做了一个demo验证上面的规则,确实如此:Demo跳转,这个demo其实就是在15秒之内一直执行、创建微任务or宏任务。在这期间去点击页面交互、看页面是否及时渲染。结果发现只有promise才会造成页面的卡顿,其他都不会(基于这个原因我更倾向把rAF规律为宏任务)。此外setTimeout还有最小4ms的限制(第一次执行除外)。也就容易理解React调度器scheduler为什么用MessageChanel来实现还有Vue的nextTick因此MessageChannel是一对一通信,没有像setTimeout受到4ms限制,还有rAF受到浏览器渲染频率限制(16.6ms)。
部分代码如下
constbutton2=document.querySelector('.btn2');button2.addEventListener('click',()=>{//可以交互,不会造成页面卡顿letpre=performance.now();letcount=0;constfn=()=>{count++;if(count%250===0){console.log('count',count);}if(performance.now()-pre<1000*15){setTimeout(()=>{fn();},0);}};fn();});不同宿主环境JavaScript有不同的运行时环境,比如浏览器、Node、WebWorker等各种环境也为JS提供创建异步任务的API。上面说的是浏览器环境,比如Node环境还有setImmediateprocess.nextTick等,不同环境Eventloop也有差别。这里不做重点介绍。
Reference规范文档
原文:https://juejin.cn/post/7098334897936269348