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));
}