Esta es una pregunta interesante para llegar al fondo.
Cuando haces esto:
verifier(3,4).then(...)
que devuelve una nueva promesa que requiere otro ciclo de regreso al bucle de eventos antes de que la promesa recién rechazada pueda ejecutar el .catch()controlador que sigue. Ese ciclo adicional da la siguiente secuencia:
verifier(5,4).then(...)
una oportunidad de ejecutar su .then()controlador antes que la línea anterior .catch()porque ya estaba en la cola antes de que el .catch()controlador de la primera entra en la cola y los elementos se ejecutan desde la cola en orden FIFO.
Tenga en cuenta que si usa el .then(f1, f2)formulario en lugar del .then().catch(), se ejecuta cuando lo espera porque no hay ninguna promesa adicional y, por lo tanto, no hay ninguna marca adicional involucrada:
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response (3,4): ", response),
(error) => console.log("error (3,4): ", error)
);
verifier(5, 4)
.then((response) => console.log("response (5,4): ", response))
.catch((error) => console.log("error (5,4): ", error));
Tenga en cuenta que también etiqueté todos los mensajes para que pueda ver de qué verifier()llamada provienen, lo que hace que sea mucho más fácil leer el resultado.
ES6 Spec sobre pedido de devolución de llamada de promesa y explicación más detallada
La especificación ES6 nos dice que los "trabajos" de promesa (como llama a una devolución de llamada desde a .then()o .catch()) se ejecutan en orden FIFO según el momento en que se insertan en la cola de trabajos. No nombra específicamente a FIFO, pero especifica que los nuevos trabajos se insertan al final de la cola y los trabajos se ejecutan desde el principio de la cola. Eso implementa la ordenación FIFO.
PerformPromiseThen (que ejecuta la devolución de llamada desde .then()) conducirá a EnqueueJob, que es la forma en que se programa la ejecución del controlador de resolución o rechazo. EnqueueJob especifica que el trabajo pendiente se agrega al final de la cola de trabajos. Luego, la operación NextJob extrae el elemento del frente de la cola. Esto asegura el orden FIFO en los trabajos de servicio de la cola de trabajos de Promise.
Entonces, en el ejemplo de la pregunta original, obtenemos las devoluciones de llamada para la verifier(3,4)promesa y la verifier(5,4)promesa insertadas en la cola de trabajos en el orden en que se ejecutaron porque ambas promesas originales se cumplieron. Luego, cuando el intérprete vuelve al bucle de eventos, primero retoma el verifier(3,4)trabajo. Esa promesa se rechaza y no hay devolución de llamada para eso en el verifier(3,4).then(...). Entonces, lo que hace es rechazar la promesa que verifier(3,4).then(...)regresó y que hace que el verifier(3,4).then(...).catch(...)controlador se inserte en jobQueue.
Luego, vuelve al bucle de eventos y el siguiente trabajo que extrae de jobQueue es el verifier(5, 4)trabajo. Eso tiene una promesa resuelta y un controlador de resolución, por lo que llama a ese controlador. Esto hace response (5,4):que se muestre la salida.
Luego, vuelve al bucle de eventos y el siguiente trabajo que extrae de jobQueue es el verifier(3,4).then(...).catch(...)trabajo donde lo ejecuta y esto hace error (3,4)que se muestre el resultado.
Es porque .catch()en la 1ª cadena hay un nivel de promesa más profundo en su cadena que .then()en la 2ª cadena que provoca el pedido que informó. Y es porque las cadenas de promesa se atraviesan de un nivel al siguiente a través de la cola de trabajos en orden FIFO, no de forma sincrónica.
Recomendación general sobre la confianza en este nivel de detalle de la programación
Para su información, en general, trato de escribir código que no dependa de este nivel de conocimiento detallado del tiempo. Si bien es curioso y ocasionalmente útil de entender, es un código frágil ya que un simple cambio aparentemente inocuo en el código puede conducir a un cambio en el tiempo relativo. Entonces, si el tiempo es crítico entre dos cadenas como esta, entonces prefiero escribir el código de una manera que fuerce el tiempo de la manera que quiero en lugar de confiar en este nivel de comprensión detallada.