首页 > web前端 > js教程 > 正文

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

碧海醫心
发布: 2025-08-17 23:02:33
原创
659人浏览过

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');
登录后复制

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

Flawless AI
Flawless AI

好莱坞2.0,电影制作领域的生成式AI工具

Flawless AI 32
查看详情 Flawless AI
  1. setTimeout 被添加到调用栈。
  2. setTimeout 被推送到 WebAPIs,并从调用栈中弹出。
  3. Promise.resolve 被识别为异步函数,其回调函数被推入微任务队列。
  4. console.log('3') 被推入调用栈并执行,输出 3。
  5. 调用栈为空,事件循环将微任务队列中的 Promise.then 回调函数推入调用栈并执行,输出 2。
  6. 调用栈为空,事件循环将任务队列中的 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 的回调函数仍然在任务队列中等待执行。

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

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

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

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

总结与注意事项

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

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

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

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号