javascript宏微任务及事件循环

11 篇文章 0 订阅
10 篇文章 0 订阅
  首先要牢记一点:JS 是一门单线程语言,在执行过程中永远只能同时执行一个任务,任何异步的调用都只是在模拟这个过程,或者说可以直接认为在 JS 中的异步就是延迟执行的同步代码。另外别的什么 Web worker、浏览器提供的各种线程都不会影响这个点。

大家应该都知道执行 JS 代码就是往执行栈里 push 函数(不知道的自己搜索吧),那么当遇到异步代码的时候会发生什么情况?
其实当遇到异步的代码时,只有当遇到 Task、Microtask 的时候才会被挂起并在需要执行的时候加入到 Task(有多种 Task) 队列中。
在这里插入图片描述

从图上我们得出两个疑问:

什么任务会被丢到 Microtask Queue 和 Task Queue 中?它们分别代表了什么?
Event loop 是如何处理这些 task 的?

首先我们来解决问题一。
Task(宏任务):同步代码、setTimeout 回调、setInteval 回调、IO、UI 交互事件、postMessage、MessageChannel。
MicroTask(微任务):Promise 状态改变以后的回调函数(then 函数执行,如果此时状态没变,回调只会被缓存,只有当状态改变,缓存的回调函数才会被丢到任务队列)、Mutation observer 回调函数、queueMicrotask 回调函数(新增的 API)。
宏任务会被丢到下一次事件循环,并且宏任务队列每次只会执行一个任务。
微任务会被丢到本次事件循环,并且微任务队列每次都会执行任务直到队列为空。
假如每个微任务都会产生一个微任务,那么宏任务永远都不会被执行了。
接下来我们来解决问题二。
Event Loop 执行顺序如下所示:

执行同步代码
执行完所有同步代码后且执行栈为空,判断是否有微任务需要执行
执行所有微任务且微任务队列为空
是否有必要渲染页面
执行一个宏任务

如果你觉得上面的表述不大理解的话,接下来我们通过代码示例来巩固理解上面的知识:

console.log('script start');

setTimeout(function() {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
    queueMicrotask(() => console.log('queueMicrotask'))
    console.log('promise');
});

console.log('script end');
  • 遇到 console.log 执行并打印
  • 遇到 setTimeout,将回调加入宏任务队列
  • 遇到 Promise.resolve(),此时状态已经改变,因此将 then 回调加入微任务队列
  • 遇到 console.log 执行并打印

此时同步任务全部执行完毕,分别打印了 ‘script start’ 以及 ‘script end’,开始判断是否有微任务需要执行。

  • 微任务队列存在任务,开始执行 then 回调函数
  • 遇到 queueMicrotask,将回到加入微任务队列
  • 遇到 console.log 执行并打印
    检查发现微任务队列存在任务,执行 queueMicrotask 回调
  • 遇到 console.log 执行并打印

此时发现微任务队列已经清空,判断是否需要进行 UI 渲染。

  • 执行宏任务,开始执行 setTimeout 回调
  • 遇到 console.log 执行并打印

执行一个宏任务即结束,寻找是否存在微任务,开始循环判断…
其实事件循环没啥难懂的,理解 JS 是个单线程语言,明白哪些是微宏任务、循环的顺序就好了。
最后需要注意的一点:正是因为 JS 是门单线程语言,只能同时执行一个任务。因此所有的任务都可能因为之前任务的执行时间过长而被延迟执行,尤其对于一些定时器而言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值