js中【微任务】和【宏任务】长篇解读

在 JavaScript 中,理解微任务(microtasks)宏任务 (macrotasks)是掌握异步编程和事件循环(Event Loop)机制的关键。这两个概念影响了代码的执行顺序,特别是在涉及异步操作(如 setTimeoutPromiseasync/await 等)时。为了深刻理解它们的差异及其在事件循环中的表现,我们将从最基础的概念出发,逐步解释这些任务的执行机制。

1. JavaScript 的单线程与异步模型

首先,JavaScript 是一门单线程语言,意味着它在同一时刻只能执行一个任务。这一特性使得 JavaScript 在执行同步代码时很容易理解:代码按顺序一行一行地执行,直到所有代码执行完毕。然而,现代应用往往需要处理异步任务,例如网络请求、定时器回调、用户输入等。为了解决这一问题,JavaScript 引入了异步机制,通过事件循环(Event Loop)调度和管理异步任务。

2. 事件循环(Event Loop)的基本概念

JavaScript 的事件循环是处理异步操作的核心机制。事件循环的工作流程如下:

  1. 执行同步任务:JavaScript 引擎首先执行所有的同步任务,这些任务会立即进入调用栈(Call Stack)并被逐个执行。
  2. 处理异步任务:当遇到异步任务时(例如 setTimeout、Promise、I/O 操作等),这些任务不会立即执行,而是被放入相应的任务队列中(宏任务队列或微任务队列)。
  3. 检查微任务队列:当所有同步任务执行完毕,调用栈为空时,事件循环会优先检查并执行微任务队列中的所有任务。
  4. 执行宏任务:在微任务队列清空后,事件循环从宏任务队列中取出下一个宏任务并执行。每次执行完一个宏任务后,事件循环会再次检查微任务队列,依此反复进行。

3. 宏任务(Macrotasks)与微任务(Microtasks)

在 JavaScript 中,任务分为宏任务(macrotask)微任务(microtask)。它们的区别主要体现在任务调度和执行顺序上。

宏任务(Macrotasks)

宏任务是事件循环的主任务,包括所有涉及异步操作的主要任务。宏任务由浏览器或 Node.js 环境调度,并在每轮事件循环中处理一个宏任务。常见的宏任务包括:

  • setTimeoutsetInterval
  • I/O 操作(例如网络请求回调)
  • DOM 事件(如 clickkeydown 等事件)
  • postMessage(在多个窗口或 iframe 之间通信)
  • MessageChannel
  • requestAnimationFrame

这些任务通常需要一定的等待时间(即使是 setTimeout(fn, 0)),并会在事件循环的某个阶段被处理。

微任务(Microtasks)

微任务的执行优先级比宏任务高。每当一个宏任务完成后,事件循环会立即执行微任务队列中的所有任务,只有在微任务队列清空后,才会继续执行下一个宏任务。常见的微任务包括:

  • Promise.then()Promise.catch()Promise.finally()
  • MutationObserver(观察 DOM 变化)
  • queueMicrotask()(用于将函数显式添加到微任务队列)

微任务的优势在于它们可以在当前事件循环周期内尽快执行,确保任务能够快速完成,尤其适合异步操作的回调处理。

4. 事件循环中的任务调度机制

同步任务执行流程

事件循环的第一步是执行所有的同步任务,即将同步代码依次压入调用栈(Call Stack),并按照顺序执行。当调用栈为空时,事件循环将开始处理异步任务。此时,事件循环会检查两个任务队列:

  1. 微任务队列(Microtask Queue)
  2. 宏任务队列(Macrotask Queue)
微任务优先原则

当同步任务执行完毕后,事件循环会首先检查微任务队列。如果微任务队列中有任务,事件循环会一次性执行所有的微任务(即清空微任务队列)。只有当微任务队列为空时,事件循环才会执行宏任务队列中的第一个任务。

宏任务执行流程

每次从宏任务队列中取出一个任务执行后,事件循环会再次检查微任务队列。如果微任务队列不为空,则会立即执行所有的微任务。在微任务队列为空后,才会回到宏任务队列继续处理下一个宏任务。

因此,微任务的执行优先级高于宏任务。即使一个宏任务已经准备好执行,微任务队列中的任务仍会先被执行。

5. 例子:理解微任务与宏任务的执行顺序

console.log('start'); // 同步任务setTimeout(() => {console.log('setTimeout'); // 宏任务
}, 0);Promise.resolve().then(() => {console.log('promise1'); // 微任务
}).then(() => {console.log('promise2'); // 微任务
});console.log('end'); // 同步任务

执行顺序解析:

  1. 同步任务:首先执行 console.log('start'),输出 start
  2. 同步任务:继续执行 console.log('end'),输出 end
  3. 微任务队列Promise.resolve() 的回调被放入微任务队列。事件循环执行完同步任务后,开始执行微任务队列中的任务,先输出 promise1,再输出 promise2
  4. 宏任务队列setTimeout() 的回调被放入宏任务队列。所有微任务完成后,事件循环执行宏任务,输出 setTimeout

最终输出顺序为:

start
end
promise1
promise2
setTimeout

6. queueMicrotask()Promise.then() 的区别

queueMicrotask()Promise.then() 都会将任务加入微任务队列,但它们有一些细微的差别:

  • queueMicrotask():这是一个直接将任务添加到微任务队列的函数,适用于需要在当前事件循环周期内尽快执行的任务。
  • Promise.then():当 Promise 被 resolve 时,其 .then() 回调会被添加到微任务队列。

两者的执行时机基本一致,但 queueMicrotask() 提供了显式控制微任务的能力。

queueMicrotask(() => {console.log('microtask');
});setTimeout(() => {console.log('timeout');
}, 0);Promise.resolve().then(() => {console.log('promise');
});

输出顺序为:

microtask
promise
timeout

尽管 setTimeout 的延迟是 0,但微任务 microtaskpromise 仍会优先于宏任务 timeout 执行。

7. 在 Node.js 环境中的区别

在浏览器环境中,微任务和宏任务的执行顺序如上所述。然而,在 Node.js 中,事件循环的机制略有不同。Node.js 的事件循环包括多个阶段,每个阶段处理特定类型的任务。

Node.js 中有一个特殊的微任务机制:process.nextTick(),它的优先级甚至高于微任务。process.nextTick() 的回调会在微任务之前执行,因此它可以用于需要更高优先级的任务。

process.nextTick(() => {console.log('nextTick');
});Promise.resolve().then(() => {console.log('promise');
});setTimeout(() => {console.log('timeout');
}, 0);

输出顺序为:

nextTick
promise
timeout

process.nextTick() 的回调会在 Promise.then() 回调之前执行。

8. 常见的微任务和宏任务陷阱

1. setTimeout(fn, 0) 并不会立即执行

setTimeout(fn, 0) 并不会在 0 毫秒后立即执行,而是会将回调放入宏任务队列中。由于微任务具有更高优先级,Promise.then()queueMicrotask() 等微任务会先于 setTimeout 执行。

2. 微任务过多可能导致性能问题

由于微任务总是会在每个事件循环周期内被优先执行,因此如果有过多的微任务被不断添加,它们可能会阻塞宏任务的执行,导致浏览器无法响应用户的交互。

总结

  1. 同步任务:优先执行。
  2. 微任务:每当同步任务执行完

毕或宏任务结束后,立即执行所有微任务。
3. 宏任务:微任务执行完后,才会执行下一个宏任务。
4. 微任务优先于宏任务:在每次事件循环中,微任务总是先于宏任务执行。
5. Node.js 中的 process.nextTick():其回调优先级比微任务更高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/53545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

postgres--系统视图,表

pg_stat_all_tables 累积统计系统-监控数据库活动 pg_stat_all_tables是一个系统视图,它为当前数据库中的每个表(包括TOAST表)提供一行数据。显示了关于特定表的访问统计信息,比如访问次数等。pg_stat_user_tables和pg_stat_sys_…

根据NVeloDocx Word模板引擎生成Word(三)

基于永久免费开放的《E6低代码开发平台》的Word模版引擎NVeloDocx,实现根据Word模版生成Word文件,前面2篇已经非常详细介绍了《主表单字段》,《子表记录循环输入到表格》。那这一篇我们就介绍插入单张图片、二维码,条形码等等&…

python-网页自动化(三)

如果遇到使用 ajax 加载的网页,页面元素可能不是同时加载出来的,这个时候尝试在 get 方法执行完 成时获取网页源代码可能并非浏览器完全加载完成的页面。所以,这种情况下需要设置延时等待一定时间,确保全部节点都加载出来。 那么&…

邦飞利减速机有哪些系列

邦飞利减速机拥有多个系列,以满足不同领域和场景的需求。以下是邦飞利减速机的一些主要系列: W系列蜗轮蜗杆减速机: 如W63、W75、W86、W110等型号,广泛应用于各种需要减速传动的设备中。VF系列微型蜗轮蜗杆减速机: 包…

搜维尔科技:AcuMap - 针灸模拟VR训练解决方案

AcuMap第一个针灸VR训练解决方案。全面且医学上准确的人体映射,其中 14 条经络和 361 个穴位以 3D 形式可视化。您还可以在虚拟人体模型上模拟针刺。主要特点 - 可视化 14 条经络及其分支 - 基于 4 个主要类别的 361 个穴位:五行、危险穴位、补邪和特殊穴…

P5745【深基附B例】区间最大和

思路一&#xff1a;枚举区间头尾i&#xff0c;j&#xff0c;然后对i和j里面所有数字累加起来求和&#xff0c;再判断是否在不大于M的情况下最大。 #include<iostream> using namespace std; int a[8000005]; int main() {int n, M, ansm 0, ai, aj;cin >> n >…

【Petri网导论学习笔记】Petri网导论入门学习(一)

Petri 网导论 如需学习转载请注明原作者并附本帖链接&#xff01;&#xff01;&#xff01; 如需学习转载请注明原作者并附本帖链接&#xff01;&#xff01;&#xff01; 如需学习转载请注明原作者并附本帖链接&#xff01;&#xff01;&#xff01; 发现网上关于Petri网的学习…

【机器学习】从零开始理解深度学习——揭开神经网络的神秘面纱

1. 引言 随着技术的飞速发展,人工智能(AI)已从学术研究的实验室走向现实应用的舞台,成为推动现代社会变革的核心动力之一。而在这一进程中,深度学习(Deep Learning)因其在大规模数据处理和复杂问题求解中的卓越表现,迅速崛起为人工智能的最前沿技术。深度学习的核心是…

金智维K-RPA基本介绍

一、K-RPA基本组成 K-RPA软件机器人管理系统基于“RPAX”数字化技术打造&#xff0c;其核心系统由管理中心(Server)、设计器(Control)、机器人(Robot/Agent)三大子系统组成&#xff0c;各子系统协同工作&#xff0c;易于构建协同式环境。 管理中心&#xff08;Server&#xff…

【Linux 运维知识】Linux 编译后的内核镜像大小

Linux 内核镜像的大小取决于多个因素&#xff0c;包括内核的版本、启用的功能、模块的数量以及特定的编译配置。 以下是常见情况下不同内核镜像的大小范围&#xff1a; 1. 标准内核镜像大小 压缩后的内核镜像 (vmlinuz)&#xff1a; 压缩后的内核镜像文件&#xff0c;通常位于…

《MaPLe: Multi-modal Prompt Learning》论文解读

系列文章目录 文章目录 系列文章目录论文细节理解1、研究背景2、论文贡献3、方法框架4、研究思路5、实验6、限制 论文细节理解 预训练的视觉语言&#xff08;V-L&#xff09;模型&#xff0c;如CLIP&#xff0c;已经显示出对下游任务的出色泛化能力。然而&#xff0c;它们对输入…

基于boost的共享内存通信demo

文章目录 前言一、共享内存管理二、图像算法服务中的IPC通信流程三、demo实验结果总结 前言 在一个系统比较复杂的时候&#xff0c;将模块独立成单独的进程有助于错误定位以及异常重启恢复&#xff0c;不至于某个模块发生崩溃导致整个系统崩溃。当通信数据量比较大时&#xff…

AI视频生成-一键创作动漫

一、前言 随着深度学习技术和计算能力的进步&#xff0c;AI生成视频&#xff08;AIGV&#xff09;已经从一个研究概念演变成了一种实用工具&#xff0c;其应用场景也在不断拓展。从自动合成新闻报道到虚拟人物的互动视频&#xff0c;从电影特效生成到游戏场景的实时渲染&#…

C++基础知识6 vector

vector 1.vector的介绍及使用1.1 vector的介绍1.2 vector的使用1.2.1 vector的定义1.2.2 vector iterator 的使用1.2.3 vector 常用的接口1.2.4 vector 空间增长问题1.2.4 vector 迭代器失效问题。&#xff08;重点&#xff09; 2.vector模拟实现 1.vector的介绍及使用 1.1 ve…

Dubbo精要

1、为什么需要 Dubbo&#xff1f; 分布式系统中的服务调用和协调问题&#xff1a;在分布式系统中&#xff0c;服务之间的相互依赖会导致复杂的通信和协调问题。Dubbo提供了高效的服务调用和自动注册、发现等功能&#xff0c;使得构建分布式应用程序更加容易。服务治理和服务调…

局域网设备自动发现常用方法

文章目录 需求实现方法ARP (Address Resolution Protocol)Ping ip的流程抓包如下代码实现 mDNS 对比测试Avahi 介绍Avahi 安装Avahi 使用测试代码 需求 局域网设备自动发现是软件开发中的一个常见且重要的需求&#xff0c;它简化了设备间的协作机制&#xff0c;降低了软件各模…

实验九 多线程的处理

实验目的及要求 目的&#xff1a;理解线程的概念&#xff0c;掌握Java的多线程机制&#xff0c;会用多线程编写Java程序。 要求&#xff1a; 理解线程的概念会用Thread类创建线程会使用Runnable接口创建多线程对两种实现多线程方式的方式进行对比掌握线程的同步 二、实验环境…

Vue 中实现视频播放的艺术

随着前端技术的飞速发展&#xff0c;视频播放在 Web 应用中已经成为了一个不可或缺的功能。从社交媒体平台到教育网站&#xff0c;再到在线购物平台&#xff0c;视频元素无处不在。而 Vue.js 作为当今最流行的前端框架之一&#xff0c;在实现视频播放时提供了很多强大的工具和技…

项目日志——日志器模块的设计、同步日志器实现、测试

文章目录 日志器模块设计同步日志器实现测试 日志器模块 设计 日志器模块的主要功能就是将前面的所有模块进行整合&#xff0c;向外提供接口&#xff0c;完成不同等级日志的输出 需要管理的成员有 格式化模块的对象落地模块的对象组当前默认的输出等级互斥锁日志器名称各个…

Pygame中Sprite类实现多帧动画3-3

4 使用自定义类MySprite 使用自定义类MySprite实现多帧动画的步骤是首先创建MySprite类的实例&#xff0c;之后使用相关函数对该实例进行操作。 4.1 创建MySprite类的实例 创建MySprite类的实例的代码如图12所示。 图12 创建MySprite类的实例的代码 其中&#xff0c;变量dr…