JavaScript 事件循环:任务队列与微任务队列的执行顺序详解

javascript 事件循环:任务队列与微任务队列的执行顺序详解

本文深入探讨 JavaScript 事件循环中的任务队列(Task Queue)和微任务队列(Job Queue/Microtask Queue)的执行顺序。通过分析 setTimeout 和 Promise 的交互,揭示了即使微任务队列优先级更高,依赖于任务队列中任务的微任务也必须等待其依赖的任务执行完毕后才能执行。本文将通过代码示例详细解释这一过程,并提供相关注意事项,帮助开发者更好地理解 JavaScript 的异步机制。

深入理解 JavaScript 事件循环

JavaScript 是一种单线程语言,这意味着它一次只能执行一个任务。为了处理异步操作,JavaScript 使用事件循环机制。事件循环不断地检查调用栈是否为空,如果为空,则从任务队列中取出第一个任务放入调用栈中执行。

事件循环主要包含以下几个关键组成部分:

调用栈(Call Stack): 用于执行同步代码。任务队列(Task Queue/Callback Queue): 用于存放异步任务的回调函数,例如 setTimeout、setInterval、事件监听器等。也称宏任务队列。微任务队列(Job Queue/Microtask Queue): 用于存放微任务的回调函数,例如 Promise.then、MutationObserver 等。WebAPIs: 浏览器提供的 API,例如 setTimeout、DOM 事件等。

任务队列与微任务队列的优先级

微任务队列的优先级高于任务队列。这意味着,当调用栈为空时,事件循环会首先检查微任务队列,如果微任务队列中有任务,则会优先执行微任务队列中的所有任务,直到微任务队列为空,才会去执行任务队列中的任务。

立即学习“Java免费学习笔记(深入)”;

setTimeout 与 Promise 的交互

setTimeout 的回调函数会被放入任务队列中,而 Promise.then 的回调函数会被放入微任务队列中。因此,通常情况下,Promise.then 的回调函数会比 setTimeout 的回调函数更早执行。

考虑以下代码:

setTimeout(() => {  console.log('1');}, 0);Promise.resolve('2').then(console.log);console.log('3');

这段代码的执行顺序如下:

setTimeout 被添加到调用栈。setTimeout 被推送到 WebAPIs,并从调用栈中弹出。Promise.resolve 被识别为异步函数,其回调函数被推入微任务队列。console.log(‘3’) 被推入调用栈并执行,输出 3。调用栈为空,事件循环将微任务队列中的 Promise.then 回调函数推入调用栈并执行,输出 2。调用栈为空,事件循环将任务队列中的 setTimeout 回调函数推入调用栈并执行,输出 1。

因此,最终输出结果为 3 2 1。

依赖关系的影响

现在,考虑以下代码:

setTimeout(() => {  console.log('1');}, 0);Promise.resolve(setTimeout(() => {  console.log('2');}, 0));console.log('3');

这段代码的执行顺序有所不同。Promise.resolve 接收的是 setTimeout 的返回值(timeout ID),而不是 setTimeout 回调函数本身。这意味着 Promise.resolve 立即被解析,并将一个已经完成的 Promise 放入微任务队列。而 setTimeout 的回调函数仍然在任务队列中等待执行。

因此,这段代码的执行顺序如下:

setTimeout 被添加到调用栈。setTimeout 被推送到 WebAPIs,并从调用栈中弹出。Promise.resolve 被识别为异步函数,其回调函数被推入微任务队列。console.log(‘3’) 被推入调用栈并执行,输出 3。调用栈为空,事件循环将微任务队列中的 Promise.then 回调函数推入调用栈并执行。由于 Promise 已经 resolve,所以 Promise.then 立即执行,但是它并没有输出任何东西,因为它接收的是 setTimeout 的 ID。调用栈为空,事件循环将任务队列中的第一个 setTimeout 回调函数推入调用栈并执行,输出 1。调用栈为空,事件循环将任务队列中的第二个 setTimeout 回调函数推入调用栈并执行,输出 2。

因此,最终输出结果为 3 1 2。

关键在于,即使 Promise.then 的回调函数在微任务队列中,但它依赖于 setTimeout 的执行结果(虽然这里只是 setTimeout 的 ID,但仍然存在依赖关系)。因此,setTimeout 必须先执行,Promise.then 才能完成。

总结与注意事项

JavaScript 事件循环是理解异步编程的关键。微任务队列的优先级高于任务队列。即使微任务队列优先级更高,依赖于任务队列中任务的微任务也必须等待其依赖的任务执行完毕后才能执行。在编写异步代码时,要仔细考虑任务之间的依赖关系,以确保代码按照预期的顺序执行。避免在微任务中执行耗时操作,以免阻塞事件循环。

通过理解 JavaScript 事件循环的机制,我们可以更好地编写高效、可靠的异步代码。

以上就是JavaScript 事件循环:任务队列与微任务队列的执行顺序详解的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1515847.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 09:38:09
下一篇 2025年12月20日 09:38:14

相关推荐

  • React自定义导航返回需双击问题排查与解决

    本文针对React应用中使用自定义导航时,出现“返回按钮需要点击两次才能生效”的问题,进行了深入分析。通过排查代码逻辑和利用React Strict Mode的特性,定位问题根源在于useEffect的重复执行。文章提供了两种解决方案:一是添加条件判断避免重复执行,二是优化代码逻辑,减少对useEf…

    2025年12月20日
    000
  • React自定义导航返回需双击问题排查与解决方案

    正如摘要所述,本文旨在解决React应用中使用自定义导航时,需要双击返回按钮才能正确返回上一页的问题。以下将深入分析问题原因并提供解决方案。 问题分析 提供的代码片段展示了一个自定义的React Hook useMyHook,用于管理应用的状态,并将其同步到浏览器的URL和localStorage中…

    2025年12月20日
    000
  • js怎么判断对象是否是数组

    判断一个javascript对象是否是数组,最推荐的方法是使用array.isarray()。1. array.isarray(value)是es5引入的内置方法,能准确判断值是否为数组,包括跨iframe创建的数组;2. typeof无法区分数组和普通对象,因为typeof[]返回”o…

    2025年12月20日
    000
  • js 怎么监听浏览器窗口大小变化

    监听javascript中浏览器窗口大小变化主要通过window.addeventlistener(‘resize’, callback)实现,需绑定resize事件到window对象并执行回调函数;为兼容不同浏览器,获取窗口宽高可使用window.innerwidth、do…

    2025年12月20日
    000
  • JS如何实现函数式数据结构?纯函数实现

    在javascript中实现函数式数据结构的核心是通过不可变性和纯函数确保每次操作都返回新数据副本而不修改原数据,具体可通过原生方法如map、filter、concat、展开运算符及object.assign实现数组和对象的不可变操作,对于复杂结构可使用类或工厂函数构建自定义不可变数据结构如不可变栈…

    2025年12月20日
    000
  • js 怎样解密数据

    前端javascript解密数据的核心是使用web crypto api,1. 首先通过crypto.subtle.decrypt()调用支持aes-gcm等算法的解密方法;2. 解密前需将密钥和数据转换为cryptokey和arraybuffer格式;3. 解密后将结果转为可读字符串;4. 密钥管…

    2025年12月20日
    000
  • 什么是Portal?Portal的使用场景

    portal是一种高度集成、个性化的信息与应用入口,其本质是为用户提供“单一事实来源”和“单一操作界面”。它不同于传统网站的信息广播模式,而是基于用户身份、角色和偏好动态呈现内容,实现“千人千面”的定制化服务。传统网站重在“看”,面向大众展示固定内容;而portal重在“用”,支持登录、操作、交互,…

    2025年12月20日
    000
  • 什么是RAF?requestAnimationFrame

    requestAnimationFrame(RAF)是浏览器提供的API,用于在下一次重绘前执行动画代码,确保动画与屏幕刷新率同步,提升流畅性。它基于浏览器渲染机制,在每帧刷新前调用回调函数,避免了setTimeout或setInterval可能造成的掉帧问题。RAF在页面后台时会自动暂停,节省资源…

    2025年12月20日
    000
  • CommonJS和ES模块有什么区别

    commonjs采用同步加载,es模块采用异步加载;2. commonjs使用require和module.exports,es模块使用import和export;3. commonjs适用于node.js环境,es模块适用于浏览器及现代node.js环境;4. commonjs处理循环依赖时可能获…

    2025年12月20日
    000
  • js怎么实现原型链的动态继承

    javascript中的动态继承主要通过object.setprototypeof()、__proto__属性和object.create()实现;2. object.setprototypeof()是标准推荐方法,用于运行时修改对象原型,但可能影响性能和可维护性;3. __proto__为非标准且…

    2025年12月20日 好文分享
    000
  • JS如何实现Hook测试?Hook的测试方案

    javascript中实现hook测试的核心方法包括猴子补丁、proxy对象、测试框架的mock/spy功能和装饰器,其中最推荐的是使用jest等现代测试框架提供的mock/spy功能,因其封装了底层机制,提供了安全、可维护的api,并能自动管理生命周期;猴子补丁虽简单直接但易污染全局环境,需手动恢…

    2025年12月20日
    000
  • js怎么获取屏幕分辨率

    获取屏幕分辨率应使用window.screen.width和window.screen.height,它们返回显示器的物理像素尺寸;而浏览器视口尺寸则通过window.innerwidth/window.innerheight或document.documentelement.clientwidth…

    2025年12月20日
    000
  • 什么是性能分析?Profiler的工具

    性能分析的核心在于通过Profiler工具从宏观到微观定位软件性能瓶颈,首先明确性能目标,再利用工具收集CPU、内存、I/O等运行数据,分析热点函数或资源消耗点,进而优化代码并反复验证,形成迭代优化过程;其重要性体现在提升用户体验、降低服务器成本、增强系统可伸缩性,并反映代码质量;常见的Profil…

    2025年12月20日
    000
  • JS如何实现预加载?资源的预加载

    答案:JS通过动态创建link标签或Image对象等方式实现资源预加载,核心依赖浏览器的preload、prefetch等机制,结合用户行为与关键资源优先级,精准提升页面加载速度与用户体验。 JS实现资源预加载,核心在于提前获取用户可能需要但当前页面尚未完全加载的资源,从而在实际使用时减少等待时间,…

    2025年12月20日
    000
  • 如何利用事件循环优化CPU密集型任务?

    利用事件循环优化cpu密集型任务的核心是将其从主线程剥离,避免阻塞事件循环导致应用无响应;2. 浏览器中使用web workers在后台线程执行计算,通过postmessage通信,保持主线程流畅;3. node.js中可选worker threads(轻量、高效、适合频繁交互的计算任务)或chil…

    2025年12月20日 好文分享
    000
  • js 如何调用摄像头

    javascript调用摄像头需先通过navigator.mediadevices.getusermedia请求用户授权,获取视频流并显示在video标签中;2. 优化体验时应在请求前提示用户目的,提供取消选项,并引导用户手动开启权限以防浏览器不再弹出请求框;3. 兼容性问题可通过引入adapter…

    2025年12月20日
    000
  • JS如何实现完美哈希?完美哈希的构造

    完美哈希是一种针对固定键集的无冲突哈希技术,通过预计算生成唯一索引映射,确保O(1)最坏情况查找性能。在JavaScript中,它通常以离线计算的查找表或映射对象形式使用,如{ “if”: 0, “else”: 1 },适用于编译器关键字匹配等静态场景…

    2025年12月20日
    000
  • 如何通过URL查询参数在不同HTML页面间传递数据

    本教程详细阐述了如何在不同HTML页面之间传递数据,特别聚焦于使用URL查询参数的方法。我们将通过一个点餐系统示例,演示如何从一个菜单页面获取商品名称和价格,并通过点击按钮将其安全地传递到支付页面,并在支付页面自动填充相应的表单输入框。文章涵盖了数据编码、URL构建以及在目标页面解析和使用这些数据,…

    2025年12月20日
    000
  • JS如何实现图像识别

    答案:JavaScript通过TensorFlow.js等库调用预训练模型实现图像识别,利用WebAssembly和WebGL加速,在浏览器端完成推理任务。这种方式保护用户隐私、降低服务器成本、支持离线使用,但受限于设备性能和模型大小,适合轻量级、实时性要求高的场景。 JavaScript(JS)实…

    2025年12月20日
    000
  • js怎么获取url的hash值

    获取url的hash值最直接的方法是使用window.location.hash,它返回包含#号及其后内容的字符串,若无hash则返回空字符串;2. 若需去除#号,可使用substring(1)截取#之后的内容;3. 处理空hash时应先判断window.location.hash是否为真,避免对空…

    2025年12月20日
    000

发表回复

登录后才能评论
关注微信