dom事件回调属于宏任务,触发时入宏队列,需等同步代码和微任务执行完后才执行;事件流三阶段均在同一次宏任务内完成,框架更新通常置于微任务末尾。

DOM 事件(比如点击、输入、滚动)在事件循环中属于宏任务,它们的回调函数被推入宏任务队列,在当前同步代码执行完、微任务清空后,才被取出执行。
DOM 事件是宏任务,不是立即执行
当你点击一个按钮,浏览器检测到事件后,并不会立刻调用绑定的 onclick 或 addEventListener 回调。它会把该回调作为一项宏任务,加入宏任务队列。这个过程发生在事件触发的“当下”,但执行要等轮到它。
- 事件本身由浏览器底层检测(如鼠标驱动、键盘中断),属于宿主环境行为
- 对应的 JS 回调函数不参与同步执行,而是排队等待调度
- 即使你连续快速点击两次,也会生成两个独立的宏任务,按顺序执行
执行时机严格遵循事件循环三步走
一次 DOM 事件回调的完整调度路径是:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 当前同步脚本(或上一个宏任务)执行完毕,调用栈清空
- 立即执行所有已排队的微任务(
Promise.then、queueMicrotask等),直到队列为空 - 从宏任务队列中取出**第一个**任务(可能是 DOM 事件回调、
setTimeout、fetch回调等),开始执行
这意味着:哪怕你在 click 回调里改了数据、又立刻 console.log,只要中间没产生微任务,DOM 更新也不会出现在这次执行中——渲染通常发生在宏任务执行完、下一轮循环开始前(且浏览器判定有变更时)。
事件流阶段不影响任务入队时机,只影响回调内 event 行为
捕获、目标、冒泡三个阶段,是在 DOM 事件回调**内部**发生的逻辑,不改变该回调何时被调用。整个事件流都在同一个宏任务中完成:
- 浏览器确定事件路径后,一次性遍历捕获 → 目标 → 冒泡各阶段
- 每个阶段上注册的监听器(
useCapture=true/false)都会在本次宏任务中被依次调用 -
event.stopPropagation()只阻止后续阶段的传播,不跳出当前宏任务
和 Vue/React 的 DOM 更新要区分清楚
框架层(如 Vue 的 ref 更新)通常把 DOM 渲染任务安排在微任务末尾(例如 Vue 的 queueFlush 走 Promise.then)。所以:
- 原生 DOM 事件回调(宏任务)里修改响应式数据 → 框架把更新任务推入微任务队列
- 该宏任务执行完 → 微任务清空 → DOM 渲染发生(如果需要)→ 下一个宏任务才开始
- 想在 DOM 更新后操作节点?必须用
await nextTick()(Vue)或queueMicrotask(() => {...})(原生)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










