preloader

Microtareas (Microtasks) en JavaScript

Microtareas (Microtasks)

Los manejadores de promesas .then/.catch/.finally son siempre asincrónicos.

Incluso cuando una promesa se resuelve inmediatamente, el código que se encuentra después de .then/.catch/.finally se ejecutará antes que estos manejadores.

Veamos un ejemplo:

				
					let promise = Promise.resolve();

promise.then(() => alert("¡Promesa cumplida!"));

alert("código completado"); // esta alerta se muestra primero

				
			

Si ejecutas esto, verás «código completado» primero, y después «¡promesa cumplida!».

Esto puede parecer extraño, porque la promesa se resuelve de inmediato.

¿Por qué .then se ejecutó después? ¿Qué está pasando aquí?

Cola de Microtareas

Las tareas asincrónicas necesitan ser gestionadas adecuadamente. Para ello, el estándar ECMA especifica una cola interna llamada PromiseJobs, a veces conocida como la «cola de microtareas» (terminología de V8).

Como se indica en la especificación:

  • La cola es de tipo primero en entrar, primero en salir (FIFO): la tarea que entra primero en la cola, será la primera en ejecutarse.
  • La ejecución de una tarea se inicia solo cuando no se está ejecutando nada más.

En términos simples, cuando una promesa está lista, sus manejadores .then/.catch/.finally se ponen en la cola; aún no se ejecutan. Cuando el motor de JavaScript termina el código actual, toma una tarea de la cola y la ejecuta.

Por eso, en el ejemplo anterior, el «código completado» se muestra primero.

Los manejadores de promesas siempre pasan por esta cola interna.

Si hay una cadena con múltiples .then/.catch/.finally, entonces cada uno de ellos se ejecuta de manera asincrónica. Es decir, primero se coloca en la cola, luego se ejecuta cuando el código actual termina y los manejadores previamente en la cola se han completado.

¿Qué pasa si el orden es importante?

¿Cómo podemos hacer que «código completado» se ejecute después de «¡promesa cumplida!»?

Es sencillo, solo ponlo en la cola con .then:

				
					Promise.resolve()
  .then(() => alert("¡promesa cumplida!"))
  .then(() => alert("código completado"));

				
			

Ahora el orden es el esperado.

Rechazo no gestionado

¿Recuerdas el evento unhandledrejection del artículo sobre manejo de errores con promesas?

Ahora podemos ver exactamente cómo JavaScript detecta un rechazo no gestionado (unhandled rejection).

Se produce un «rechazo no gestionado» cuando no se maneja un error de promesa al final de la cola de microtareas.

Normalmente, si esperamos un error, añadimos .catch a la cadena de promesas para gestionarlo:

				
					let promise = Promise.reject(new Error("¡Promesa fallida!"));
promise.catch(err => alert('error capturado'));

// No se ejecuta: error gestionado
window.addEventListener('unhandledrejection', event => alert(event.reason));

				
			

Pero si olvidamos añadir el .catch, entonces, después de que la cola de microtareas esté vacía, el motor activará el evento:

				
					let promise = Promise.reject(new Error("¡Promesa fallida!"));

// ¡Promesa fallida!
window.addEventListener('unhandledrejection', event => alert(event.reason));

				
			

Si ejecutamos esto, veremos «¡Promesa fallida!» primero y después «error capturado».

Si no supiéramos sobre la cola de microtareas, podríamos preguntarnos: «¿Por qué se ejecutó el manejador unhandledrejection? ¡Capturamos y gestionamos el error!»

Pero ahora entendemos que unhandledrejection se dispara cuando se completa la cola de microtareas: el motor examina las promesas y, si alguna está en estado «rechazado», entonces el evento se dispara.

En el ejemplo anterior, el .catch agregado por setTimeout también se dispara, pero lo hace más tarde, después de que unhandledrejection ya haya ocurrido, por lo que no cambia nada.

Resumen

El manejo de promesas es siempre asincrónico, ya que todas las acciones de promesa pasan por la cola interna de «PromiseJobs», también llamada «cola de microtareas» (terminología de V8).

Entonces, los manejadores .then/.catch/.finally siempre se llaman después de que el código actual ha finalizado.

Si necesitamos garantizar que un código se ejecute después de .then/.catch/.finally, podemos añadirlo a una llamada encadenada .then.

En la mayoría de los motores de JavaScript, incluidos los navegadores y Node.js, el concepto de microtareas está estrechamente relacionado con el «bucle de eventos» o «event loop» y las «macrotareas». Como estos no tienen relación directa con las promesas, están cubiertos en otra parte del tutorial, en el artículo «Loop de eventos: microtareas y macrotareas».

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *