微任务 宏任务 定时器模块 到底是啥?

5 篇文章 0 订阅
2 篇文章 0 订阅

任务队列

js代码中有三个队列,同步队列,宏任务队列,微任务队列。

已知js是单线程语言,它在遇到同步任务时会直接执行,遇到异步代码时,会区分成宏任务(setTimeout-定时器模块、事件回调函数) 和 微任务(promise、async await)。

所以,执行优先级就是 同步任务 > 微任务 > 宏任务。
上代码

setTimeout(() => {
  console.log("我是宏任务");
});
Promise.resolve().then(function () {
  console.log("我是微任务");
});
console.log("我是主线程");
// 猜猜执行结果吧?
// 我是主线程 我是微任务 我是宏任务  

我们让代码再复杂一点,增加一个局部环境

首先在全局环境中,先输出同步内容【我是主线程】,接下来是执行全局下的微任务【我是微任务1】,然后是执行全局环境下的宏任务【我是宏任务】。后面在setTimeout里我们新增了一个局部环境的promise,装在宏任务的setTimeout中。这是个微任务,由于同步队列中没有任何任务,所以执行输出【我是微任务2】。

看起来好像全局环境比局部环境的执行优先级更高,但其实不然。

setTimeout里的微任务2的代码比微任务1的代码更靠前,但微任务1还是被打印在前头。

是因为setTimeout本质是宏任务,执行优先级比下面的微任务1微任务更低,所以在 【我是微任务1】被打印出来时,【我是微任务2】才刚刚被执行到。

如果把setTimeout换成 promise的话,那结果就不一样了。毕竟promise是同步代码,会阻塞代码,直到里面的同步代码执行完毕后,才会继续从全局环境执行同步任务的代码。

setTimeout(() => {
  console.log("我是宏任务");
  Promise.resolve().then(function () {
    console.log("我是微任务2");
  });
});
Promise.resolve().then(function () {
  console.log("我是微任务1");
});
console.log("我是主线程");
// 猜猜执行结果吧?
// 我是主线程 我是微任务1 我是宏任务 我是微任务1

ok,很不错,让我们在复杂一点!

setTimeout(() => {
  console.log("我是宏任务");
  Promise.resolve().then(function () {
    console.log("我是微任务2");
  });
});
async function hd() {
  let a = await new Promise(function (resolve, rej) {
    console.log("我是小机灵");
    resolve("我是async");
  });
  console.log(a);
}
hd();
Promise.resolve().then(function () {
  console.log("我是微任务1");
});
console.log("我是主线程");
// 先不说答案,大家一起算一算

首先,分析下代码任务类型。最开始的setTimeout是宏任务,不用说,肯定是最后再执行的;
接下来,我们定义了一个async函数,那么这个函数会是异步的还是同步的呢?它里面还包含一个await的Promise函数,那么是会先打印 a 呢?还是打印 “我是小机灵” 呢。
在这里插入图片描述

因为最后几行代码,一个是微任务,一个是同步任务在这里插入图片描述
所以,我们着重分析中间那些代码。

首先,被async定义的函数,本质还是同步任务,只不过await 会把后面的代码挂起,相当于弄出一个微任务,所以 变量 a 不会被先打印出来,反而promise中的函数是同步任务,“我是小机灵”会优先的,并视作同步任务给打印出来。

所以上面的代码执行的打印结果是

【我是小机灵】【我是主线程】【我是async】【我是微任务1】【我是微任务2】【我是宏任务】
在这里插入图片描述

俗话说光说不练假把式,那么这种技术在我们前端代码写作时有什么意义呢?

比如你写了一个商城的【确认订单】页面。。。

想想一下,当你第一次登录某商城时,满心期待的点击确认按钮,按下的速度仿佛能穿越时间,直接把你送到收到商品的那一刻。可惜商城却提示你

“请填写收货地址”

你心里想,好吧,虽然麻烦,但不写的话就会什么也收不到了,反正也就这一个了!
于是,在写完收货地址后,你陆陆续续选择了积分、优惠券和用一元换购了一个保温杯。
作为程序员的你,一定能想到,这地址的获取,积分的兑换,优惠券的金额与换购物品的展示都不会是空穴来风,更不可能是前端写死在代码里。那就肯定是后端发送过来的数据。
但是后端不可能用意念知道你需要什么数据,你的交互一定要及时且精准。
并且,在你把优惠券相关数据传给后端时,他们还想知道积分的使用情况和收货人住在什么地方……
此时,一个实现依赖着另外两个实现,就出现了层级。在层级的后面,就是异步和同步的完美嵌套。

此时,会出现什么问题?

1.后端返回缺少积分或地址。那么你就看是不是获取积分和地址的接口的回调是否被你塞在微任务队列中,而此时的获取优惠券的接口就已经丢出去了。
解决方法就是使用 .then去包住获取优惠券接口的代码,或者用await挂起这部分代码,以实现按流程执行。

ps
promise 拥有状态中转和状态不可逆的特点。状态中转就是将promise对象传递给另一个promise对象的resolve状态,那么上一个promise的成功或失败的状态都会影响到接收的promise对象的执行结果。无法用reject接。
promise一单执行成功或失败状态后,就不能在执行另外的一个状态,因为执行了resolve或reject后,任务就进入到微任务队列里了,所以是不可逆的。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值