Cuando el evento de clic se activa desde el mouse, se comporta como se esperaba:
Primero, el oyente 1 se inserta en la pila donde pone en cola la promesa 1 en Microtask Queue (o Job Queue). Cuando se quita el oyente 1, la pila se vacía. Y la devolución de llamada de la promesa 1 se ejecuta antes que el oyente 2 (que está esperando en la Cola de tareas (o Cola de devolución de llamada). Después de que se retira la devolución de llamada de la promesa 1, el oyente 2 se inserta en la pila. Por lo tanto, el resultado es:
Oyente 1 Microtarea 1 Oyente 2 Microtarea 2
Sin embargo, cuando el clic se activa a través del código JavaScript, se comporta de manera diferente:
La devolución de llamada se inserta en la pila incluso antes de que se complete la función click() (es decir, la pila de llamadas no está vacía). La salida aquí es:
Oyente 1 Oyente 2 Microtarea 1 Microtarea 2
Aquí está el código:
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>Tengo entendido que los elementos de Task Queue y Microtask Queue solo se envían a Call Stack cuando está vacío. Podría haber hecho suposiciones equivocadas. Por favor, siéntase libre de corregirme. Gracias
Sus observaciones son correctas y la explicación es bastante sencilla:
La cola de microtareas solo se visita cuando la pila de contexto de ejecución de JavaScript (también conocida como pila de llamadas) está vacía (definida en la limpieza después de ejecutar una secuencia de comandos , a la que llama la operación de un usuario ). Técnicamente, el ciclo de eventos tiene bastantes llamadas para realizar un punto de control de microtarea , pero los casos en los que la pila de llamadas JS no puede estar vacía son tan raros que pueden ignorarse.
El envío de un evento a través de eventTarget.dispatchEvent() es síncrono , no se pone en cola ninguna tarea nueva, las devoluciones de llamada solo se llaman desde el contexto actual y, además, la pila de llamadas JS no está vacía .
const target = new EventTarget(); target.addEventListener("foo", () => console.log("new foo event")); console.log("before"); target.dispatchEvent(new Event("foo")); console.log("after");Por lo tanto, la cola de microtareas tampoco se visita, solo será después de que se complete la pila de llamadas JS, que en su código es cuando el trabajo del controlador del evento de clic original se ejecuta por completo.
Sin embargo, los eventos enviados "de forma nativa" por el motor crearán un nuevo trabajo JS por devolución de llamada y, por lo tanto, entre cada uno de ellos, la pila de llamadas JS estará vacía y se visitará la cola de microtareas.
Mientras se esté ejecutando <button onclick> , .then() no se ejecutará.
Este fragmento muestra un poco mejor la diferencia en el orden de ejecución:
window.onload = function() { [document.body, myBtn].forEach(node => { node.addEventListener("click", function(e) { const { currentTarget, target } = e; new Promise((r) => { console.log( "Promise()", currentTarget.tagName ) r(); }).then(() => { console.log( ".then()", currentTarget.tagName ) }); }); }); } <button id="myBtn" onclick="console.log('hello world');">Manual Click</button> <button onclick="myBtn.click(); console.log('I will happen before the then');">JS click</button>