如何让JavaScript不同事件监听器按指定异步顺序执行?
解决事件监听器异步任务的顺序问题
问题原因
当前代码的执行顺序不符合预期,核心原因有两点:
- 事件冒泡机制:点击按钮时,按钮的点击事件监听器先执行(同步代码输出
(1) first),然后事件冒泡到父元素div,执行其监听器的同步代码(输出(3) third)。 - 异步任务队列:
setTimeout属于宏任务,会被放入事件队列等待同步代码执行完毕后再处理。由于div监听器中的setTimeout延迟时间更短(100ms),它会比按钮监听器中200ms的任务先执行,因此输出(4) next task早于(2) task which takes longer but must done earlier。
解决方案
要让div的监听器等待按钮的异步任务完成后再执行,我们可以利用Promise + async/await来实现任务的顺序控制:
修改后的index.js代码如下:
// 用于存储按钮异步任务的Promise let buttonTaskPromise = Promise.resolve(); let resolveButtonTask; document.getElementById("btn").addEventListener("click", () => { console.log("(1) first"); // 重置Promise,确保每次点击都对应新的任务 buttonTaskPromise = new Promise(resolve => { resolveButtonTask = resolve; }); setTimeout(() => { console.log("(2) task which takes longer but must done earlier"); resolveButtonTask(); // 任务完成时resolve Promise }, 200); }); document.getElementById("dv").addEventListener("click", async () => { // 等待按钮的异步任务完成 await buttonTaskPromise; console.log("(3) third"); setTimeout(() => { console.log("(4) next task"); }, 100); });
代码说明
- Promise管理:我们维护一个
buttonTaskPromise变量,每次点击按钮时创建新的Promise,当按钮的异步任务完成时调用resolveButtonTask()来标记任务完成。 - async/await等待:
div的监听器被声明为async函数,通过await buttonTaskPromise等待按钮的异步任务完成后,再执行自身的同步和异步代码。 - 处理重复点击:每次点击按钮都会重置Promise,确保后续点击不会受到之前任务的影响。
额外说明
- 如果需要处理直接点击
div而非按钮的情况,可以在div的监听器中判断事件源:document.getElementById("dv").addEventListener("click", async (e) => { // 如果点击的不是按钮,直接执行后续逻辑 if (e.target.id !== "btn") { console.log("(3) third"); setTimeout(() => { console.log("(4) next task"); }, 100); return; } await buttonTaskPromise; console.log("(3) third"); setTimeout(() => { console.log("(4) next task"); }, 100); });
内容的提问来源于stack exchange,提问作者Juyeon
相关产品推荐
相关产品推荐

