6

Callbacks will never be called before the completion of the current run of the JavaScript event loop.

fuente: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

En la versión en español:

Las funciones callback nunca serán llamadas antes de la terminación de la ejecución actual del bucle de eventos de JavaScript.

¿A qué se refiere con eso de que los callbacks adheridos a una promesa no serán ejecutados antes de que se complete la ejecución actual del event loop? No entiendo muy bien esa parte

Pablo Lozano
  • 45,934
  • 7
  • 48
  • 87
  • Hola Juan. Creo que si cambias el título de tu pregunta y la modificas no la identificarán como un posible duplicado. Preguntas cómo funcionan las promesas en el título pero en el enunciado, en realidad en lo que tienes duda es en comprender qué es el bucle de eventos de `JavaScript`. Otra cosa que ayuda a hacer _flag_ de tu pregunta es que sitúas otra pregunta después pidiendo algo que es meramente una opinión pero de la que puedes encontrar mucha información en internet y en StackOverflow. Siempre intenta preguntar solo una cosa en cada pregunta. – ElChiniNet Oct 13 '19 at 21:23
  • 1
    Ya modifique el titulo, y también quite la parte que puede estar basada en opiniones – Juan Sandoval Oct 13 '19 at 21:50
  • 1
    Relacionada: https://es.stackoverflow.com/questions/166250/sincron%c3%ada-vs-asincron%c3%ada-para-recoger-datos-en-tiempo-real-con-javascript/243005#243005 – Pablo Lozano Oct 14 '19 at 15:26

2 Answers2

4

Vamos por pasos:

Definamos callback brevemente como una función que se pasa como parámetro a otra para que esta segunda la llame. Asumo que el OP conoce el concepto así que no entraremos en detalles.

Modelo de concurrencia de Javascript:

  • Lo primero de todo es entender que no se puede parar la ejecución del código una vez ésta ha empezado, la ejecución se ejecutará hasta que el código termine de manera normal o provoque un error que no sea capturado. Esto puede provocar que la interfaz de un navegador aparentemente deje de responder a las acciones del usuario, porque esas acciones no obtienen respuesta, ya que el código asociado a dichas acciones pasa a la cola de ejecución, esperando su turno. En resumen, se podría decir que toda ejeción ocurre dentro de un bucle de eventos:

    while (colaEventos.esperaMensaje()) {
      colaEventos.procesaSiguienteMensaje();
    }
    
  • Si el hilo actual de ejecución termina, se empezará a ejecutar el siguiente de la cola de eventos. Si la cola está vacía, pues no se ejecutará nada y el sistema se quedará a la espera de que algún evento añada código a la cola.

Aquí hemos de aclarar que cuando llamamos a setTimeout o setInterval no añadimos eventos a la cola directamente, se añadirán los eventos cuando haya pasado el tiempo determinado en las llamadas.

Un ejemplo de código que añade eventos sería la función setTimeout(callback, time): esta función añadirá a la cola de eventos el código de la función callback dentro de time milisegundos.

let i=1;

while (i < 5) {
  console.log('Añado a la cola de eventos un setTimeout, i vale',i);
  setTimeout(() => console.log(i));
  i++;
}

Aquí vemos que vamos encolando llamadas a console.log(), pero estas no se ejecutan hasta que el bucle termina. Para cuando el bucle termina i vale 5 y por tanto se nos muestra 5 4 veces, una para cada evento encolado.

Este comportamiento es el que tienen las promesas: incluso aunque su ejecución pueda ser completada inmediatamente, el código no se ejecutará hasta que el hilo actual termine. En el ejemplo siguiente vemos que las promesas son completadas inmediatamente, pero el código del callback (lo que le pasamos mediante el método then), se añade a la cola de eventos:

var i;

for (i=0;i<5;i++) {
   console.log('Creando promesa con i=',i);
   new Promise(function(completa) {
       console.log('Resolviendo para i',i);
       completa(i);
   }).then(resultado => console.log('Obtenemos',resultado));

}

Nota importante: Al definir una promesa así, tenemos dos callbacks: una es la función completa, que se ejecuta de manera síncrona e inmediata, y otra que es la que le pasamos a then, que es asíncrona (se añade a la cola del bucle de eventos). Es esta última a la que hace referencia la documentación de la pregunta. Se ve más claro si creamos promesas con async:

async function creaPromesa(param) {
  return param;
}

let i=0;

for (;i<5;i++) {
  creaPromesa(i).then((dato) => console.log('Resolviendo con', dato));
}
Pablo Lozano
  • 45,934
  • 7
  • 48
  • 87
2

Primero que todo, es bueno que leas la referencia al bucle de eventos (event loop). Después que lo hayas leído, creo que entenderás perfectamente a qué se refiere si te lo explico con dos snippets:

Edición: En una discusión en meta acerca de esta pregunta salió a relucir esta otra y es bueno que la consultes también porque las respuestas son muy valiosas.

Callbacks tradicionales:

const hazAlgoMas = (numero) => {
  console.log(`se ha llamado a hazAlgoMas con valor: ${numero}`);
  return numero * 2;
}

const hazAlgo = (numero, callback) => {
  console.log(`se ha llamado a hazAlgo con valor: ${numero}`);
  return callback(++numero);
}

for (let i = 1; i <= 5; i++) {
  const resultado = hazAlgo(i, hazAlgoMas)
  console.log(`el resultado es: ${resultado}`);
}

Observa cómo hazAlgo y hazAlgoMas se ejecutan una detrás de la otra en cada iteración del ciclo.

Usando promesas:

const hazAlgoMas = (numero) => {
  console.log(`se ha llamado a hazAlgoMas con valor: ${numero}`);
  return new Promise(resolve => resolve(numero * 2));
}

const hazAlgo = (numero) => {
  console.log(`se ha llamado a hazAlgo con valor: ${numero}`);
  return new Promise(resolve => resolve(++numero));
}

for (let i = 1; i <= 5; i++) {
  hazAlgo(i)
    .then(hazAlgoMas)
    .then(resultado => console.log(`el resultado es: ${resultado}`));
}

Como puedes ver, en este caso, el bucle de eventos está organizado de manera diferente, primero en la cola están las llamadas a hazAlgo, sin importar que hazAlgoMas y el console.log están también declarados dentro del ciclo.

Después están las llamadas a hazAlgoMas una detrás de la otra y por último todos los console.log con los resultados. Como puedes notar, los callbacks no han sido ejecutados como en el ejemplo anterior, el ciclo crea diferentes mensajes en el bucle de eventos con llamadas a la función hazAlgo y como esta retorna una promesa, los callbacks de cada una de estas llamadas se ejecutarán después de que se hayan procesado todos esos mensajes creados anteriormente. Esto es a lo que se refiere el siguiente enunciado:

los callbacks adheridos a una promesa no serán ejecutados antes de que se complete la ejecución actual del event loop.

Dependiendo de la arquitectura de tu aplicación esto puede ser útil para asegurarte que algún dato no ha cambiado porque algún callback lo ha variado sin que lo desearas.

ElChiniNet
  • 3,215
  • 9
  • 25