JavaScript事件机制依事件循环协调异步执行,微任务(如Promise.then、queueMicrotask)总在宏任务(如setTimeout、click)结束后立即清空且保序;宏任务按队列先进先出但受延迟和浏览器调度影响;async/await基于微任务保障串行顺序;DOM事件触发则遵循捕获→目标→冒泡三阶段及添加顺序。

JavaScript 事件机制通过事件循环(Event Loop)协调多个异步事件的触发顺序,核心在于区分任务类型、排队规则和执行时机。它不靠“谁先注册就先执行”,而是严格遵循微任务优先、宏任务轮转、队列先进先出的原则。
微任务总在宏任务之后立即清空
Promise.then、catch、finally、queueMicrotask() 等产生的任务属于微任务,它们被推入微任务队列。每当一个宏任务(如 setTimeout 回调、事件处理器、脚本整体)执行完毕,引擎会一次性执行完当前所有微任务,中间不穿插渲染或下一个宏任务。
- 哪怕连续调用 10 次 queueMicrotask,它们也会按调用顺序全部执行,且都在当前宏任务结束后的同一轮中
- Promise 链中的每个 .then 都生成新微任务,因此链式调用天然保序
- 微任务不会被中断——一旦开始执行微任务队列,必须清空才进入下一步
宏任务按注册顺序排队,但受执行时机影响
setTimeout、setInterval、I/O 回调、用户事件(click、input)等属于宏任务,进入宏任务队列。浏览器每轮事件循环只取一个宏任务执行,但实际触发顺序 ≠ 注册顺序,因为:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- setTimeout 的延迟是“至少等待”,若主线程正忙,回调可能延后数毫秒甚至更久
- 用户点击事件由浏览器在帧刷新间隙统一收集并入队,多个点击可能合并为一个批次处理
- 不同来源的宏任务可能分属不同队列(如 UI 事件队列、定时器队列),但最终由事件循环统一调度
async/await 是语法糖,底层仍走 Promise 微任务
await 表达式暂停的是当前 async 函数的执行,但不会阻塞线程;它等价于把后续代码包装进 .then(),因此await 后的语句本质上是微任务。这保证了串行逻辑的强顺序性:
立即学习“Java免费学习笔记(深入)”;
- 正确:await fetch('/a'); await fetch('/b'); → b 一定在 a 完成后才发起请求
- 错误:Promise.all([fetch('/a'), fetch('/b')]) → a 和 b 并发发出,无先后依赖
- try/catch 可捕获单个 await 的 reject,不影响外层流程控制流
事件监听器的触发顺序取决于事件流阶段
对于 DOM 事件(如 click),顺序还受事件传播机制影响:
- 捕获阶段:从 window → document → body → 目标父元素(由外向内)
- 目标阶段:到达绑定事件的元素本身
- 冒泡阶段:从目标元素 → 父元素 → document → window(由内向外)
- 相同阶段、同级监听器按添加顺序执行(addEventListener 第三个参数 false 即冒泡阶段,默认行为)

















