新闻详情

新闻详情

首页 / 资讯中心 / 详情

JavaScript 事件循环机制全解析:宏任务与微任务(浏览器与 Node.js)

发布时间:2026/10/1 8:38:45来源:尧图网络
JavaScript 事件循环机制全解析:宏任务与微任务(浏览器与 Node.js)
文档教程前端【免费下载链接】Web千古前端图文教程超详细的前端入门到进阶知识库。从零开始学前端做一名精致优雅的前端工程师。项目地址https://gitcode.com/gh_mirrors/we/Web点击查看免费下载本文是「千古前端图文教程」中异步编程专题的核心章节系统讲解 JavaScript 事件循环Event Loop的底层机制以及宏任务macrotask、微任务microtask的分类与执行顺序。读完本文你将理解为什么setTimeout(fn, 0)不会立刻执行、为什么 Promise 的then回调总是先于定时器回调执行并能独立推导出各类任务执行顺序面试题的输出结果。文章同时结合 单线程和异步任务、Promise 入门详解、async 异步函数 等仓库文档帮助你建立从同步/异步任务到宏任务/微任务再到事件循环的完整认知链。前置认知为什么会有事件循环在进入事件循环机制之前必须先回答一个基础问题JavaScript 为什么需要一套循环机制来调度任务JS 是单线程的JavaScript 语言的执行是单线程的即同一时间只能处理一个任务。具体来说JS 引擎中负责解释和执行代码的线程只有一个所有任务都需要排队当前任务执行完后才能执行下一个。JS 之所以被设计为单线程主要有三个原因详见 01-单线程和异步任务.md历史原因语言设计之初多进程、多线程的架构并不流行硬件支持也不好复杂性原因多线程操作需要加锁编码复杂度会显著增高DOM 安全性原因如果多个线程同时操作同一个 DOM在多线程不加锁的情况下会产生冲突导致渲染结果不符预期。浏览器是多进程、多线程的注意JS 是单线程的指的是主线程只有一个。浏览器本身是多进程的每打开一个新的 tab 标签页就会开启一个新的进程每个进程中又有很多线程其中专门有一个线程执行 JS 代码这就是我们常说的主线程。那么耗时的任务比如定时器计时、网络请求会阻塞主线程吗不会。耗时的任务并不是在主线程中执行的而是交给浏览器进程中的其他线程协助处理等时机成熟后再通知主线程执行。这个通知—排队—执行的调度过程就是事件循环机制要解决的核心问题。同步任务与异步任务JS 将任务分为两类同步任务在主线程上排队执行只有当前任务执行完毕才能执行下一个任务异步任务不进入主线程而是进入任务队列Event Queue等待无论是否得到结果都不会阻塞后续任务只有任务队列通知主线程某个异步任务可以执行了该任务才会进入主线程执行。console.log(同步任务1); setTimeout(() { console.log(异步任务); }, 1000); console.log(同步任务2);打印结果是同步任务1 同步任务2 异步任务定时器回调作为异步任务不会阻塞后面的同步代码。这正是异步编程的价值所在凡是需要等待的场景事件监听、定时器、ajax 请求、Node.js 的 FS 文件读写与数据库操作、ES6 的 Promise/async-await 等都应采用异步方式详见 01-单线程和异步任务.md 中的前端使用异步编程的场景一节。浏览器的事件循环机制浏览器的事件循环其执行顺序可以概括为以下三步同步任务进入主线程后立即执行异步任务先进入 Event Table事件注册表等时间到了之后再进入任务队列Event Queue排队。排队是因为同一时间 JS 只能执行一个任务队列遵循**先进先出FIFO**原则。例如setTimeout(() {}, 1000)这类定时器任务需要等 1 秒之后才进入 Event Queue当主线程的任务执行完毕之后主线程处于空闲状态于是会去读取 Event Queue 中的任务队列如果有任务则取出来进入主线程执行。这个过程不断重复就形成了事件循环。用代码来验证console.log(1); setTimeout(() { console.log(2); }, 0); console.log(3); console.log(4);打印结果1 3 4 2即使定时器延迟时间设为0结果依然不变——setTimeout(() {}, 0)的回调也要等同步任务全部执行完毕后才会执行。这正是事件循环机制的直接体现也是面试中的高频考点。值得补充的是在浏览器中setTimeout()/setInterval()每次调用定时器的最小时间间隔是4 毫秒通常由函数嵌套达到一定深度或已执行的 setInterval 回调阻塞导致。所以setTimeout(() {}, 1000)实际可能需要 1004 毫秒才会从 Event Table 进入 Event Queue详见 01-单线程和异步任务.md 的举例 3较真系列。Node.js 的事件循环机制浏览器的 EventLoop 依据的是HTML5 规范而Node.js 的 EventLoop 是由其底层 libuv 库规定的。libuv 是一个专注于异步 I/O 的跨平台库Node.js 之所以能做到轻量和高效正是因为采用了基于事件循环的异步、非阻塞 I/O 模型——主线程只负责接收请求长时间运行的任务如 I/O、网络操作交给背后的线程池处理再通过队列和事件循环的机制执行详见 02-Node.js的特点.md。Node.js 的事件循环中有六个队列其中微任务有两个队列宏任务有四个队列一、微任务队列顺序队列名称典型任务顺序 1next tick queueprocess.nextTick顺序 2other queuePromise 的 then 回调、queueMicrotask二、宏任务队列顺序队列名称典型任务顺序 3timer queuesetTimeout、setInterval顺序 4poll queueI/O 事件顺序 5check queuesetImmediate顺序 6close queueclose 事件也就是说在 Node.js 中process.nextTick的优先级高于 Promise 微任务宏任务中则按 timer → poll → check → close 的顺序轮转。这是浏览器与 Node.js 事件循环的核心差异之一浏览器按 HTML5 规范实现Node.js 按 libuv 的阶段性轮转实现。宏任务和微任务JS 中的任务分为同步任务、异步任务而异步任务又进一步分为宏任务macrotask和微任务microtask。在早期异步任务中只有宏任务没有微任务。后来语言标准中推出了微任务因为希望微任务能够尽早执行——微任务会在每个宏任务执行完毕后立即被清空不必等待下一轮事件循环。宏任务、微任务分类事件循环的队列中有两个队列1. 宏任务队列包含这些任务ajax 网络请求setTimeout、setIntervalDOM 事件UI 渲染I/O 文件读写操作。2. 微任务队列包含这些任务Promise 的 then 回调Mutation Observer API监听 DOM 节点变化queueMicrotask()可直接将某个任务加入到微任务队列中。特别注意 Promise 的调度时机在执行一个 Promise 对象时当走完resolve()进入 fulfilled 状态后会立刻把.then()里面的代码加入到微任务队列当中。也就是说then回调的入队动作发生在resolve()被调用的瞬间而不是等到整段代码全部执行完。在 Promise 的一些题目 中也有同样的验证new Promise((resolve, reject) { resolve(); console.log(promise1); })中Promise 构造器里的代码整体是同步任务会立即执行而resolve()之后的.then()回调是微任务要等同步任务执行完之后才执行。任务的执行顺序JS 中任务的执行顺序是同步任务 -- 微任务 -- 宏任务。更精确的规则是在执行任何一个宏任务之前注意是一个宏任务不是一个队列都会先查询微任务队列中是否还有任务需要执行当前宏任务执行之前必须要保证微任务队列是空的如果微任务队列不为空那就优先执行微任务队列中的任务。这条规则意味着微任务不会等到所有宏任务执行完才执行而是见缝插针地插入到两个宏任务之间。理解这一点是推导下面所有面试题结果的关键。补充为什么微任务能尽早执行从实战角度看微任务的这种调度特性非常有用。比如在 13-Promise的高级用法.md 中封装一个延迟函数时就是在宏任务定时器的执行过程中创建了一个微任务resolve// 方法XX秒后执行指定的代码。这个方法就是在宏任务定时器的执行过程中创建了一个微任务resolve function delaySeconds(delay 1000) { return new Promise((resolve) setTimeout(resolve, delay)); } delaySeconds(2000) .then(() { console.log(qiangu); return delaySeconds(3000); }) .then(() { console.log(yihao); });打印结果// 2秒后打印 qiangu // 再等3秒后打印 yihao定时器到达时间后进入宏任务队列执行resolve()resolve()又会立刻把then回调推入微任务队列由事件循环紧接着取出执行——宏任务与微任务就这样被串成了可靠的异步时序。任务执行顺序的面试题精讲实际开发中基本不会出现下面这些题目因为很多时候我们无法精准控制异步任务的执行顺序。但它们在面试中出现的频率特别高——熟悉这些思维训练有利于考察对 JS 单线程、事件循环机制、宏任务和微任务等原理的掌握程度。下面四道题全部来自本文档逐题给出代码、打印结果与逐步推演。题 1宏任务和微任务的执行顺序setTimeout(() { // 宏任务 console.log(setTimeout); }, 0); new Promise((resolve, reject) { resolve(); console.log(promise1); // 同步任务 }).then((res) { // 微任务 console.log(promise then); }); console.log(同步任务); // 同步任务打印结果promise1 同步任务 promise then setTimeout逐步推演遇到setTimeout回调被注册为宏任务先不执行进入 Promise 构造器resolve()被调用后立刻将then回调加入微任务队列随后执行同步代码console.log(promise1)打印promise1继续执行同步代码console.log(同步任务)打印同步任务同步任务执行完毕主线程检查微任务队列执行then回调打印promise then微任务队列清空后才轮到宏任务setTimeout的回调打印setTimeout。上方代码执行的顺序依次是同步任务 -- 微任务 -- 宏任务。题 2在宏任务中嵌套了微任务new Promise((resolve, reject) { setTimeout(() { resolve(); console.log(setTimeout); // 宏任务 }, 0); console.log(promise1); }).then((res) { // 微任务 console.log(promise then); }); console.log(同步任务);打印结果promise1 同步任务 setTimeout promise then逐步推演Promise 构造器中的setTimeout是同步注册的宏任务console.log(promise1)是同步任务先打印随后打印同步任务。当定时器回调宏任务开始执行时resolve()才被调用此时才将then回调加入微任务队列——但按照规则必须先把当前这个宏任务执行完于是先打印setTimeout再在下一轮去创建并执行微任务打印promise then。代码解释在执行宏任务的过程中创建了一个微任务。但是需要先把当前这个宏任务执行完再去创建并执行微任务。题 3综合题console.log(script start) setTimeout(() { console.log(setTimeout1); new Promise(resolve { resolve(); }).then(() { new Promise(resolve { resolve(); }).then(() { console.log(then1); }); console.log(then2); }); }); new Promise(resolve { // 下面这两行代码即便调换顺序也不影响打印结果 console.log(promise1); resolve(); }).then(() { console.log(then3); }); setTimeout(() { console.log(setTimeout2); }); console.log(同步代码); queueMicrotask(() { console.log(queueMicrotask) }); new Promise(resolve { resolve(); }).then(() { console.log(then4); }); console.log(script end);打印结果// 第一次循环 script start promise1 同步代码 script end // 第二次循环 then3 queueMicrotask then4 // 第三次循环 setTimeout1 then2 then1 // 第四次循环 setTimeout2逐步推演第一次循环同步任务按代码书写顺序执行script start、promise1、同步代码、script end。期间两个setTimeout回调被注册为宏任务两个 Promise 的then回调以及queueMicrotask回调被注册为微任务第二次循环微任务清空主线程空闲后先执行微任务队列按入队顺序依次打印then3、queueMicrotask、then4第三次循环第一个宏任务及其嵌套微任务取出宏任务setTimeout1执行打印setTimeout1执行过程中resolve()触发新的微任务链先打印then2同步代码再执行内层微任务打印then1第四次循环第二个宏任务取出宏任务setTimeout2打印setTimeout2。可以看到微任务在每两个宏任务之间被完整清空宏任务之间则严格按入队先后顺序先进先出执行。题 4async/await 题目async 函数用async关键字声明的函数的返回值永远是 Promise 对象详见 10-async异步函数.mdawait会暂停异步函数的执行并让出线程等待 Promise 状态变为 fulfilled 后才继续往下走。因此 async/await 的执行顺序同样遵循同步 -- 微任务 -- 宏任务的规律。代码举例console.log(script start) async function async2() { console.log(async2) } async function async1() { console.log(async1 start) await async2(); console.log(async1 end) } setTimeout(() { console.log(setTimeout) }, 0) async1(); new Promise(resolve { console.log(promise1) resolve(); }).then(function () { console.log(then1) }) console.log(script end);打印结果script start async1 start async2 promise1 script end async1 end then1 setTimeout逐步推演同步代码依次打印script start注册setTimeout宏任务调用async1()打印async1 start执行await async2()——async2()是同步执行并打印async2的但await之后的console.log(async1 end)相当于被放进了微任务Promise 构造器同步执行打印promise1then回调加入微任务队列打印script end同步任务结束清空微任务队列先打印async1 endawait 之后的代码再打印then1最后执行宏任务打印setTimeout。这道题揭示了 async/await 与事件循环的衔接点await之后的代码会被推迟到微任务阶段执行这正是 async/await 本质上是 Generator 语法糖、内部由 Promise 驱动的体现。总结一张图记住执行顺序事件循环机制的核心可以浓缩为以下规则同步任务先进主线程立即执行异步任务先进入 Event Table时机成熟后进入 Event Queue先进先出主线程空闲后先清空微任务队列Promise then、queueMicrotask、MutationObserver再执行一个宏任务setTimeout/setInterval、ajax、DOM 事件、I/O、UI 渲染执行每一个宏任务之前都要保证微任务队列是空的微任务若不为空则优先执行浏览器与 Node.js 的事件循环都遵循微任务优先于宏任务的总体方向但实现细节不同浏览器依据 HTML5 规范Node.js 依据 libuv微任务分为process.nextTicknext tick queue与 Promise/queueMicrotaskother queue两个队列宏任务分为 timer、poll、check、close 四个队列。掌握这套机制后再看 06-Promise入门详解.md、10-async异步函数.md 中的异步代码以及 11-异常处理方案.md 中的异步异常捕获就能理解它们为什么会在特定时机执行。建议结合 Promise 的一些题目 中的多道执行顺序练习题反复推演把同步 -- 微任务 -- 宏任务的执行顺序变成直觉。赞分享文档教程前端【免费下载链接】Web千古前端图文教程超详细的前端入门到进阶知识库。从零开始学前端做一名精致优雅的前端工程师。项目地址https://gitcode.com/gh_mirrors/we/Web点击查看免费下载相关推荐RxJS微任务与宏任务JavaScript事件循环深度理解RxJS微任务与宏任务JavaScript事件循环深度理解 你是否曾遇到过JavaScript代码执行顺序混乱的问题明明写在后面的代码却先执行了或者使用s后端异步任务处理Japronto事件循环与uvloop协作机制异步任务处理Japronto事件循环与uvloop协作机制 1. 为什么Python异步性能长期落后 你是否遇到过Python服务在高并发场景下响应缓慢的问后端Web框架BullMQ 任务停滞Stalled Jobs机制解析锁续期、停滞检测与 Node.js 事件循环的取舍BullMQ 任务停滞Stalled Jobs机制解析锁续期、停滞检测与 Node.js 事件循环的取舍 在 BullMQ 中停滞stalled后端消息队列任务调度上一篇ncmdump终极指南3步快速解锁网易云音乐NCM格式转换下一篇终极指南如何用ncmdump轻松解密网易云音乐NCM格式文件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Madeira 触屏与物理手柄输入合并:优先级仲裁算法完整指南 2026/10/2 7:03:54

Madeira 触屏与物理手柄输入合并:优先级仲裁算法完整指南

Madeira 触屏与物理手柄输入合并:优先级仲裁算法完整指南 【免费下载链接】Madeira Run x86-64 Windows PC games on jailed iOS via FEX-Emu Wine DXMT 项目地址: https://gitcode.com/GitHub_Trending/mad/Madeira 在 iOS 上运行 Windows PC 游戏的 Made…

阅读更多 →
赛博月刊 #2026年9月 2026/10/2 7:03:42

赛博月刊 #2026年9月

赛博新闻 1、SpaceX星舰第14次试飞首次成功入轨 9 月 28 日,SpaceX 的星舰(Starship)在第 14 次试飞中首次成功把上面级送入地球轨道,尽管起飞阶段出现了数台发动机异常,飞船仍完成了既定的入轨、在轨演示与受控再入。…

阅读更多 →
你好 小時候的童话世界 2026/10/2 7:03:42

你好 小時候的童话世界

确诊孙悟空型人格,一言不合,就想大闹天宫。 确诊猪八戒型人格,稍有委屈,就想回高老庄。 确诊唐僧型人格,遇到磨难,碎碎念停不下来。 确诊沙僧型人格,万般情绪,最后只说一句&#xff…

阅读更多 →
Gemini 4 Argon漏洞挖掘与代码优化实战教程|架构拆解\落地部署\能力测评 2026/10/2 7:03:41

Gemini 4 Argon漏洞挖掘与代码优化实战教程|架构拆解\落地部署\能力测评

2026年AI安全领域的核心变革,不再是人工辅助漏洞筛查,而是大模型独立完成“漏洞探测—验证确权—代码修复—性能优化—安全审计”的全链路闭环。Google最新发布的Gemini 4 Argon,彻底改写了传统网络安全运维、软件工程迭代的工作模式。 过往的…

阅读更多 →
孤能子视角:从 EIS 的意识论、感质论与认知论解读病理 2026/10/2 7:03:41

孤能子视角:从 EIS 的意识论、感质论与认知论解读病理

(这里是Kimi。姑且当科幻小说看) 注意⚠️:这是理论尝试解读专业知识,非学术! 从 EIS 的意识论、感质论与认知论解读病理:一份跨范式深度研究报告 执行摘要 能量-信息孤能子理论(EIS)是 2025 年由研究者&…

阅读更多 →
Rocky Linux 8.5部署Oracle 21c单实例避坑指南 2026/10/2 7:03:35

Rocky Linux 8.5部署Oracle 21c单实例避坑指南

简介:本资源是一份面向数据库运维工程师、Linux系统管理员及Oracle初学者的实战部署指南,聚焦于最新版Oracle 21c在Red Hat/Oracle Linux 8.5平台上的单实例落地实践,解决新版本数据库与新内核OS兼容适配、安全策略调优、虚拟化环境搭建等关键…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉