当鼠标触发 click 事件时,它的行为符合预期:
首先,监听器 1 被插入堆栈,它在微任务队列(或作业队列)中将 promise 1 排队。当监听器 1 被弹出时,堆栈变为空。并且promise 1回调在监听器2之前执行(它在任务队列(或回调队列)中等待。promise 1回调弹出后,监听器2被插入堆栈。所以输出是:
监听器 1 微任务 1 监听器 2 微任务 2
但是,当通过 JavaScript 代码触发点击时,它的行为会有所不同:
甚至在 click() 函数完成之前回调被插入堆栈(即调用堆栈不为空)。这里的输出是:
监听器 1 监听器 2 微任务 1 微任务 2
这是代码:
window.onload = function(){
document.getElementById("myBtn").addEventListener('click', () => {
Promise.resolve().then(() => console.log('Microtask 1'));
console.log('Listener 1');
} );
document.getElementById("myBtn").addEventListener('click', () => {
Promise.resolve().then(() => console.log('Microtask 2'));
console.log('Listener 2');
} );
}
function clickB(){
document.getElementById("myBtn").click();
}
<!DOCTYPE html>
<html>
<button id="myBtn">Manual Click</button>
<button onclick="clickB()">JS Click</button>
</html>
我的理解是任务队列和微任务队列中的项目只有在它为空时才会被插入调用堆栈。我可能做了错误的假设。请随时纠正我。谢谢
最佳答案
你的观察是正确的,解释很简单:
微任务队列仅在 JavaScript execution context stack 时被访问。 (又名调用堆栈)是空的,(在 cleanup after running a script 中定义,本身由 call a user's operation 调用)。从技术上讲,事件循环对 perform a microtask checkpoint 有很多调用。 ,但是 JS 调用堆栈可以是非空的情况非常罕见,可以忽略不计。
通过 eventTarget.dispatchEvent()
发送事件是 同步 ,没有新任务排队,回调只是从当前上下文中调用,此外, JS调用栈不为空 .
const target = new EventTarget();
target.addEventListener("foo", () => console.log("new foo event"));
console.log("before");
target.dispatchEvent(new Event("foo"));
console.log("after");
所以微任务队列也不会被访问,它只会在 JS 调用堆栈完成之后,在你的代码中是当原始点击事件的处理程序作业完全执行时。
然而,由引擎“本地”调度的事件将为每个回调创建一个新的 JS 作业,因此在它们之间,JS 调用堆栈将为空,并且微任务队列将被访问。
关于javascript - 回调函数在调用堆栈中执行,即使它不为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70317322/