宏任务、微任务

说到宏任务和微任务,我们就不得不提 Event Loop

JS的本质是单线:

  1. 一般来说,非阻塞性的任务采取同步的方式,直接在主线程的执行栈完成。

  2. 一般来说,阻塞性的任务都会采用异步来执行,异步的工作一般会交给其他线程完成,然后回调函数会放到事件队列中。

在这里插入图片描述
当主线程的任务执行完了(执行栈空了),JS会去询问事件队列

执行一个宏任务(先执行同步代码)–>执行所有微任务–>UI render–>执行下一个宏任务–>执行所有微任务–>UI render–>…

根据HTML Standard,一轮事件循环执行结束之后,下轮事件循环执行之前开始进行UI render。即:macro-task任务执行完毕,接着执行完所有的micro-task任务后,此时本轮循环结束,开始执行UI render。UI render完毕之后接着下一轮循环。但是UI render不一定会执行,因为需要考虑ui渲染消耗的性能已经有没有ui变动
在这里插入图片描述

宏任务与微任务概念

宿主环境提供的叫宏任务,由语言标准提供的叫微任务,这是算比较标准也算比较好记忆的区分宏任务和微任务了。

宿主环境:

简单来说就是能使javascript完美运行的环境,只要能完美运行javascript的载体就是javascript的宿主环境。目前我们常见的两种宿主环境有浏览器和node。

我们都知道window是我们一直使用的全局对象,但其实global 是 javascript 运行时所在宿主环境提供的全局对象,在node出生前这个对象一直都存在于概念里。直到node的出现才使我们真正看到了global。

宿主环境内所有的内建或自定义的变量/函数都是 global/window 这个全局对象的属性/方法,而由宿主环境提供的也叫宏任务。

语言标准:

我们都知道JavaScript是一种编程语言,但其实JavaScript由ECMA制定标准,称之为ECMAScript,所以由语言标准提供的就是微任务,比如ES6提供的promise。

promise是ES6语言标准提供的,定时器是宿主环境提供的,所以promise会比定时器更早执行。

区别

这个就像去银行办业务一样,先要取号进行排号。
一般上边都会印着类似:“您的号码为多少号,前边还有多少人。”之类的字样。

因为柜员同时职能处理一个来办理业务的客户,这时每一个来办理业务的人就可以认为是银行柜员的一个宏任务来存在的,当柜员处理完当前客户的问题以后,选择接待下一位,广播报号,也就是下一个宏任务的开始。
所以多个宏任务合在一起就可以认为说有一个任务队列在这,里边是当前银行中所有排号的客户。

任务队列中的都是已经完成的异步操作,而不是说注册一个异步任务就会被放在这个任务队列中,就像在银行中排号,如果叫到你的时候你不在,那么你当前的号牌就作废了,柜员会选择直接跳过进行下一个客户的业务处理,等你回来以后还需要重新取号

而且一个宏任务在执行的过程中,是可以添加一些微任务的,就像在柜台办理业务,你前边的一位老大爷可能在存款,在存款这个业务办理完以后,柜员会问老大爷还有没有其他需要办理的业务,这时老大爷想了一下:“最近P2P爆雷有点儿多,是不是要选择稳一些的理财呢”,然后告诉柜员说,要办一些理财的业务,这时候柜员肯定不能告诉老大爷说:“您再上后边取个号去,重新排队”。
所以本来快轮到你来办理业务,会因为老大爷临时添加的“理财业务”而往后推。
也许老大爷在办完理财以后还想 再办一个信用卡?或者 再买点儿纪念币
无论是什么需求,只要是柜员能够帮她办理的,都会在处理你的业务之前来做这些事情,这些都可以认为是微任务。

在当前的微任务没有执行完成时,是不会执行下一个宏任务的。

在这里多嘴说一下async/await函数

为,async/await本质上还是基于Promise的一些封装,而Promise是属于微任务的一种。所以在使用await关键字与Promise.then效果类似

async函数在await之前的代码都是同步执行的,可以理解为await之前的代码属于new Promise时传入的代码,await之后的所有代码都是在Promise.then中的回调

JavaScript 中的任务(macro task)和任务(micro task)是事件循环机制中的两个核心概念,它们决定了异步任务的执行顺序。两者的主要区别在于执行的优先级和触发时机。 ### 任务 任务包括 `setTimeout`、`setInterval`、`setImmediate`(Node.js 环境)、I/O 操作、UI 渲染等。每当一个任务执行完毕后,JavaScript 引擎会检查是否有任务需要执行,并优先处理所有当前可用的任务,然后再继续执行下一个任务任务的执行流程如下: 1. 执行栈清空后,检查任务队列。 2. 执行所有当前任务。 3. 从任务队列中取出一个任务执行。 4. 重复上述流程。 例如,`setTimeout` 是一个典型的任务,它会在指定的时间后将回调函数放入任务队列中等待执行 [^2]。 ```javascript setTimeout(() => { console.log("任务"); }, 0); ``` ### 任务 任务包括 `Promise.then`、`Promise.catch`、`Promise.finally`、`async/await`、`MutationObserver`、`process.nextTick`(Node.js 环境)等。任务具有更高的优先级,它们会在当前任务结束后立即执行,而无需等待下一轮事件循环。 任务的执行流程如下: 1. 每当一个任务执行完毕后,JavaScript 引擎会立即检查任务队列。 2. 所有当前可用的任务会依次执行,直到队列为空。 3. 任务执行完成后,才会进入下一个任务的执行阶段 [^1]。 例如,`Promise.then` 是一个典型的任务,它会在当前任务执行结束后立即执行 [^2]。 ```javascript new Promise((resolve) => { console.log("同步任务"); resolve(); }).then(() => { console.log("任务"); }); ``` ### 执行顺序示例 下面是一个完整的示例,展示了任务任务的执行顺序: ```javascript console.log("同步任务 1"); setTimeout(() => { console.log("任务 1"); new Promise((resolve) => { console.log("同步任务 2"); resolve(); }).then(() => { console.log("任务 2"); }); }, 0); new Promise((resolve) => { console.log("同步任务 3"); resolve(); }).then(() => { console.log("任务 1"); }); console.log("同步任务 4"); ``` 输出结果如下: ``` 同步任务 1 同步任务 3 同步任务 4 任务 1 任务 1 同步任务 2 任务 2 ``` 在这个例子中,`Promise.then` 是任务,因此它会在当前任务结束后立即执行,而 `setTimeout` 是任务,因此它会在任务队列清空后执行 [^2]。 ### 总结 JavaScript 中的任务任务是事件循环机制的重要组成部分。任务包括 `setTimeout`、`setInterval` 等,而任务包括 `Promise.then`、`async/await` 等。任务具有更高的优先级,它们会在当前任务结束后立即执行,而任务则需要等待任务队列清空后才会执行 [^3]。 这种机制确保了异步任务的执行顺序,同时也为开发者提供了更细粒度的控制,使得代码的执行流程更加可控。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值