即使调用堆栈不为空,回调函数也会在调用堆栈中执行

One*_*nex 6 javascript callstack event-loop dom-events executioncontext

当鼠标触发单击事件时,它的行为符合预期:

首先,侦听器 1 被推入堆栈,并在微任务队列(或作业队列)中对 Promise 1 进行排队。当侦听器 1 弹出时,堆栈变空。Promise 1 回调在侦听器 2 之前执行(侦听器 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();
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>
<button id="myBtn">Manual Click</button>
<button onclick="clickB()">JS Click</button>
</html>
Run Code Online (Sandbox Code Playgroud)

我的理解是,任务队列和微任务队列中的项目仅在调用堆栈为空时才会被推入调用堆栈中。我可能做出了错误的假设。请随时纠正我。谢谢

Kai*_*ido 6

Your observations are correct and the explanation is quite straightforward:
The microtask queue only gets visited when the JavaScript execution context stack (a.k.a. call-stack) is empty, (defined in cleanup after running a script, itself called by call a user's operation). Technically the event loop has quite a few calls to perform a microtask checkpoint, but the cases where the JS call-stack can be non-empty are so rare they can be disregarded.

Dispatching an Event through eventTarget.dispatchEvent() is synchronous, no new task is queued, the callbacks are just called from the current context, and moreover, the JS call stack is not empty.

const target = new EventTarget();
target.addEventListener("foo", () => console.log("new foo event"));
console.log("before");
target.dispatchEvent(new Event("foo"));
console.log("after");
Run Code Online (Sandbox Code Playgroud)

So the microtask queue doesn't get visited either, it will only be after the JS call stack is done, which in your code is when the original click event's handler job is executed completely.

However Events dispatched "natively" by the engine will create a new JS job per callback, and thus between each of them the JS call stack will be empty, and the microtask queue will get visited.