Orden de ejecución de async/await, promesa y setTimeout (último en 2023)

Empezar con un tema

Hoy vi una pregunta de la entrevista sobre el orden de ejecución de async/await, promesa y setTimeout . La pregunta es la siguiente:

async function async1() {
	console.log('async1 start');
	await async2();
	console.log('asnyc1 end');
}
async function async2() {
	console.log('async2');
}
console.log('script start');
setTimeout(() => {
	console.log('setTimeOut');
}, 0);
async1();
new Promise(function (reslove) {
	console.log('promise1');
	reslove();
}).then(function () {
	console.log('promise2');
})
console.log('script end');

 La respuesta que di:

script start
async1 start
async2
asnyc1 end // x
promise1
script end
promise2
setTimeOut

Respuesta correcta:

script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeOut

¿Por qué aparece la promesa1 antes de que finalice asnyc1? Con esta pregunta, comencé a aprender sobre el mecanismo de bucle de eventos.

js EventLoop mecanismo de bucle de eventos

Hay dos tipos de eventos en JavaScript: 

 La secuencia de ejecución de eventos es ejecutar tareas macro primero y luego ejecutar micro tareas. Esta es la base. Las tareas pueden tener tareas sincrónicas y tareas asincrónicas. Las sincrónicas ingresan al hilo principal y las asincrónicas ingresan a la tabla de eventos y se registran. la función. Después de que se complete el evento asincrónico, colocará la función de devolución de llamada en la cola de eventos (las macro tareas y las micro tareas son colas de eventos diferentes). Después de ejecutar la tarea de sincronización, los eventos se leerán de la cola de eventos y se colocarán. en el hilo principal para su ejecución. La función de devolución de llamada también puede contener diferentes tareas, por lo que las operaciones anteriores se realizan en un bucle.

Nota: setTimeOut no coloca directamente su función de devolución de llamada en la cola asincrónica mencionada anteriormente, sino que coloca la función de devolución de llamada en la cola asincrónica de ejecución una vez que se agota el tiempo del temporizador. Si ya hay muchas tareas en esta cola en este momento, colóquelas detrás de ellas. Esto también explica por qué setTimeOut no se puede ejecutar con precisión. La ejecución de setTimeOut debe cumplir dos condiciones:

El proceso principal debe estar inactivo. Si se acaba el tiempo, el proceso principal no ejecutará su función de devolución de llamada si no está inactivo.
Esta función de devolución de llamada no se ejecutará hasta que se hayan ejecutado todas las funciones asincrónicas anteriores cuando se inserte en el cola asincrónica.

promesa, asíncrono/espera

En primer lugar, la nueva Promesa es una tarea sincrónica y se colocará en el proceso principal para su ejecución inmediata. La función .then() significa que las tareas asincrónicas se colocarán en la cola asincrónica, ¿cuándo se colocarán en la cola asincrónica? Cuando finalice el estado de su promesa, se colocará inmediatamente en la cola asincrónica.

Las funciones con la palabra clave async devolverán un objeto de promesa . Si no hay espera en él, la ejecución es equivalente a una función ordinaria; si no hay espera, la función async no es muy poderosa, ¿verdad?

La palabra clave await debe estar dentro de la función de palabra clave async. Si await se escribe fuera, se informará un error; await, al igual que su semántica, está esperando a que se complete la expresión de la derecha. En este momento, await abandonará el hilo, bloqueará el código posterior dentro de async y primero ejecutará el código fuera de async. El código posterior interno no se ejecutará hasta que se ejecute el código de sincronización externo. Incluso si await no es un objeto de promesa sino una función sincronizada, seguirá esperando esta operación.

Clasificación de procesos

Repasemos el proceso de ejecución del código anterior en su conjunto: 

  1. El fragmento de código completo (script) ejecuta console.log('inicio del script') como una tarea macro y genera el inicio del script;
  2. La ejecución de setTimeout es una acción asincrónica y se coloca en la cola asincrónica de tareas macro;
  3. Ejecute async1 (), genere el inicio de async1 y continúe con la ejecución;
  4. Ejecute async2 (), genere async2 y devuelva un objeto de promesa. Await abandona el hilo y agrega la promesa devuelta a la cola asincrónica de microtask, por lo que el código siguiente async1 () también debe esperar a que se complete lo anterior antes de continuar con la ejecución;
  5. Ejecute la nueva Promesa, genere la promesa1 y luego coloque resolve () en la cola asincrónica de microtask;
  6. Ejecute console.log('script end') y genere el final del script;
  7. En este punto, se han ejecutado todos los códigos sincronizados y luego vaya a la cola asincrónica de microtask para obtener la tarea.
  8. A continuación, se ejecuta la resolución (devuelta por la promesa devuelta por async2) y se genera el final de async1;
  9. Luego ejecute resolver (nueva Promesa) y genere la promesa2;
  10. Finalmente, se ejecuta setTimeout y se genera settimeout. 

En el paso 4, hay un mecanismo para await, que es la espera de await, que no bloqueará la ejecución de funciones externas. Si await está esperando una Promesa, el código de la Promesa aún se ejecutará sincrónicamente. Si no es un Promise, se utilizará Promise. .resolve para encapsular, async2 aquí es un método asíncrono, la impresión interna se ejecutará sincrónicamente y el código después de await async2 () se colocará en la primera posición en la cola de microtask, esperando el código de sincronización externo que se ejecutará.

Entonces sé por qué el final del script tiene prioridad sobre el final async1.

Supongo que te gusta

Origin blog.csdn.net/YN2000609/article/details/132409910
Recomendado
Clasificación