Tengo el siguiente código, que no funciona como esperaba.
const bar = () => console.log('bar') const baz = () => console.log('baz') const myPromise = new Promise((resolve, reject) => resolve('should be right after baz, before bar') ); const foo = () => { console.log('foo') setTimeout(bar, 0) myPromise.then(resolve => console.log(resolve)) baz() } foo() console.log('before bar')
El resultado:
foo baz before bar should be right after baz, before bar bar
De acuerdo con lo que está escrito en https://nodejs.dev/ , la resolución de la promesa ocurrirá justo después de la función.
Las promesas que se resuelven antes de que finalice la función actual se ejecutarán inmediatamente después de la función actual.
Así que espero que 'debería ser justo después de baz, antes de bar' justo después del final de foo()
, antes de console.log('before bar')
. ¿Alguien puede explicar por qué no?
Las devoluciones de llamadas pasadas a setTimeout
se agregan a la cola de tareas, mientras que las devoluciones de llamadas relacionadas con el cumplimiento o el rechazo de promesas se agregan a la cola de microtareas.
Estas colas solo se procesan después de la ejecución síncrona de su secuencia de comandos, es decir, el código javascript que escribió.
Como resultado, 'before bar'
se registra antes de que se invoque cualquier devolución de llamada asincrónica.
Comienza la ejecución del script.
Se definen las funciones bar
, baz
y foo
. También se llama al constructor de promesas, lo que conduce a la invocación de la función de resolve
, resolviendo la promesa de forma síncrona.
la funcion foo
se llama
'foo'
está registrado en la consolasetTimeout
, la bar
de programación se llamará de forma asíncrona. la bar
se agregará en la cola de tareasthen
se llama al método en myPromise
, programando el controlador de cumplimiento para que se invoque de forma asíncrona. Este controlador de cumplimiento se agrega en la cola de microtareasbaz
, registrando 'baz'
en la consola La función foo
termina. Controle el regreso a la siguiente línea desde donde se llamó a foo
Se ejecuta la última instrucción console.log
, registrando 'before bar'
en la consola
Finaliza la ejecución del script
En este punto, la salida de la consola es como se muestra a continuación:
foo baz before bar
y la cola de tareas contiene una tarea para ejecutar la función de bar
, mientras que la cola de microtareas contiene una microtarea para ejecutar el controlador de cumplimiento de myPromise
.
Una vez que finaliza la ejecución del script, se pueden procesar las colas de tareas y microtareas.
La cola de microtareas se procesa:
La ejecución del script es una tarea y, después de su ejecución, la cola de microtareas será la primera en procesarse, el registro 'should be right after baz, before bar'
en la consola.
Por último, se procesa la cola de tareas, se llama a la función baz
y se registra 'baz'
en la consola.
La siguiente puede ser una lectura útil para comprender la microtarea y la cola de tareas: Devoluciones de llamada asincrónicas de JavaScript - Promise y setTimeout
La documentación de MDN para Promise.prototype.then
establece (el énfasis es mío):
Una vez que se cumple o se rechaza una Promesa, la función del controlador respectivo (onFulfilled o onRejected) se llamará de forma asincrónica (programada en el bucle de subproceso actual).
Puede encontrar el siguiente fragmento esclarecedor:
Promise.resolve("I resolved").then((v) => console.log(v)); console.log("hello");
Si desea comprender el concepto asincrónico, debe ver este increíble video .
Intentaré explicarte la secuencia de cómo se ejecuta el código aquí.
En primer lugar, estamos inicializando todas las variables con funciones. Luego estamos llamando a foo()
, y luego console.log('before baz')
.
Lo que sucederá al llamar a foo
es que foo
ingresará a la pila . Y foo
está conectado a la consola.
Luego se setTimeout(bar, 0)
. setTimeout(bar, 0)
siendo asincrónico , se llamará más tarde y se colocará en la cola de devolución de llamada (se llamará más tarde cuando la pila esté vacía). Por eso no vimos la consola de bar
.
Después de esto, se myPromise.then(resolve => console.log(resolve))
. Y esta también es una llamada asíncrona . Por lo tanto, esto también se enviará a la cola de devolución de llamada.
Luego se ejecutará baz()
. Y es por eso que estamos viendo un registro de baz
después del foo
.
Ahora recuerde, tenemos console.log('before bar')
en la pila, por lo que se registrará primero. Y después de esto, se llamarán todos los elementos en la cola de devolución de llamada.
Tres cosas están registradas hasta ahora: - foo
baz
before bar
Ahora, lo bueno de las promesas es que se les da prioridad en la cola de devolución de llamada, sobre cualquier cosa. Entonces, incluso se llama a setTimeout
antes de nuestra promesa, si se ejecuta la promesa, se priorizará .
Esa es la razón por la que 'should be right after baz, before bar'
se llama antes de bar
.