接下来我将会把前端知识点进行滚动复习,预计前端知识点巩固共有五篇。
目的为夯实基础,梳理知识脉络。
如何理解EventLoop——宏任务和微任务篇
宏任务(MacroTask)引入
大部分的任务都是在主线程上执行,常见的任务有:
- 渲染事件
- 用户交互事件
- js脚本执行
- 网络请求、文件读写完成事件等
为了让这些事件有条不紊地进行,JS引擎需要对之执行的顺序做一定的安排,V8
其实采用的是一种队列的方式来存储这些任务, 即先进来的先执行
。模拟如下:
1 | bool keep_running = true; |
这里用到了一个 for 循环,将队列中的任务一一取出,然后执行,这个很好理解。但是其中包含了两种任务队列
,除了上述提到的任务队列, 还有一个延迟队列
,它专门处理诸如setTimeout/setInterval这样的定时器回调任务。
普通任务队列和延迟队列中的任务,都属于宏任务。
微任务(MicroTask)引入
对于每个宏任务而言,其内部都有一个微任务队列
。
其实引入微任务的初衷是为了解决异步回调的问题
。想一想,对于异步回调的处理,有多少种方式?总结起来有两点:
- 1.将异步回调进行宏任务队列的入队操作。
- 2.将异步回调放到当前宏任务的末尾。
采用第一种方式,执行回调的时机应该是在前面所有的宏任务完成之后
,倘若现在的任务队列非常长
,那么回调迟迟得不到执行,造成应用卡顿
。
为了规避这样的问题,V8
引入了第二种方式,这就是微任务的解决方式
。在每一个宏任务中定义一个微任务队列
,当该宏任务执行完成
,会检查
其中的微任务队列
,如果为空则直接执行下一个宏任务
,如果不为空
,则依次执行微任务
,微任务执行完成
才去执行下一个宏任务。
常见的微任务有:
- MutationObserver
- Promise.then(或.reject)
- 以 Promise 为基础开发的其他技术(比如fetch API),
- V8 的垃圾回收过程。
理解EventLoop——浏览器篇
上demo:
1 | console.log('start'); |
这样就带大家直观地感受到了浏览器环境下 EventLoop 的执行流程。不过,这只是其中的一部分情况,接下来我们来做一个更完整的总结。
- 一开始整段脚本作为第一个宏任务执行
- 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当前宏任务执行完出队,检查微任务队列,如果有则依次执行,直到微任务队列为空
- 执行浏览器 UI 线程的渲染工作
- 检查是否有Web worker任务,有则执行
- 执行队首新的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空
1 | Promise.resolve().then(()=>{ |
1 | console.log('script start') |