DOM事件处理器属于微任务还是宏任务?代码测试疑问求解
2023年8月18日更新
.click()是同步执行的- 此前代码示例的误区:Promise构造函数中的回调同样是同步执行的
- 若添加一行
Promise.resolve().then(() => console.log('then')),能更清晰地验证微任务的执行时机
DOM事件处理器执行时机解析
核心结论:通过.click()触发的DOM事件处理器是同步执行的,既不属于微任务队列也不属于宏任务队列,这是你看到两种代码输出差异的根本原因。
第一段代码输出逻辑拆解
第一段代码中,btn1的点击处理器执行流程如下:
- 同步执行
console.log('click btn1')→ 输出click btn1 - 调用
setTimeout:将回调推入宏任务队列,此时不执行 - 执行
new Promise:构造函数的回调是同步逻辑,输出promise并立即resolve(无后续.then回调,所以无微任务入队) - 调用
document.querySelector('#btn2').click():同步触发btn2的点击处理器,执行console.log('click btn2')→ 输出click btn2 - btn1的同步代码全部执行完毕后,事件循环开始处理队列:先清空微任务队列(此处为空),再处理宏任务队列中的
setTimeout回调 → 输出timer
最终输出顺序:
click btn1 promise click btn2 timer
第二段代码输出逻辑拆解
第二段代码调整了.click()的位置,btn1的点击处理器执行流程变为:
- 调用
document.querySelector('#btn2').click():同步触发btn2的点击处理器,执行console.log('click btn2')→ 输出click btn2 - 回到btn1的处理器上下文,同步执行
console.log('click btn1')→ 输出click btn1 - 调用
setTimeout:将回调推入宏任务队列 - 执行
new Promise:构造函数回调同步执行,输出promise - 同步代码执行完毕后,先处理微任务队列(此处为空),再处理宏任务队列的
setTimeout回调 → 输出timer
最终输出顺序:
click btn2 click btn1 promise timer
补充验证:加入微任务的场景
如果在代码中加入微任务逻辑,比如修改第一段代码:
document.querySelector('#btn2').addEventListener('click', () => { console.log('click btn2') }) document.querySelector('#btn1').addEventListener('click', () => { console.log('click btn1') setTimeout(() => console.log('timer')); new Promise(res => { console.log('promise');res(); }).then(() => console.log('then')); document.querySelector('#btn2').click(); })
此时输出顺序会变为:
click btn1 promise click btn2 then timer
这能更清晰地验证:所有同步代码(包括.click()触发的事件处理器)执行完成后,才会处理微任务队列中的then回调,最后处理宏任务的setTimeout,进一步坐实.click()是同步执行的结论。
内容的提问来源于stack exchange,提问作者Littlee
相关产品推荐
相关产品推荐

