¿Qué tiene de malo la implementación de jQuery de las promesas?

Supongo que está preguntando pre-jQuery 3.0. Después de jQuery 3.0, no estoy al tanto de que algo esté “mal”, pero anteriormente no cumplía con la especificación de promesas A +.

Para demostrar la diferencia, en gran parte citaré del registro de cambios de jQuery 3.0, la sección relevante es la titulada jQuery. Diferido ahora es compatible con Promesas / A +. Este artículo describe tres grandes cambios, pero en mi experiencia dos han sido mucho más importantes que el tercero.

1. Las excepciones dentro de un controlador ahora se convierten en rechazos de promesa encadenados.

Esto es un gran problema si recuerda que .then() devuelve una * nueva * promesa con un valor transformado. Esta promesa ahora puede volver a .then() . La pregunta en la tabla es qué debería suceder si una de las funciones que pasó a .then(onSuccess, onError) arroja una excepción. Así que anteriormente, si tuvieras

tratar {
const diferido = $ .Deferred ()
const promise = deferred.then (() => {
console.log (“primera devolución de llamada”)
lanzar un nuevo error (“error en devolución de llamada”)
}).luego(
() => {console.log (“segunda devolución de llamada”)},
(err) => {console.log (“rechazo de devolución de llamada”)}
)
deferred.resolve ()
} catch (err) {
console.log (“captura de devolución de llamada”)
}

Entonces, ¿qué se debe registrar en la consola aquí?

Antes de 3.0 recibía la first callback / catch callback por lo que, aunque la promesa * tenía * un controlador de errores adjunto, simplemente nunca se activó. Peor aún, si le devolvieras a alguien una instancia de promise , ¡quedaría para siempre bloqueado, ni resuelto ni rechazado!

Después de 3.0 recibimos la first callback / rejection callback ya que las excepciones se convierten en rechazos encadenados. Este es un gran problema para evitar el problema de la promesa nunca resuelta.

2. Resolución asincrónica

Este me ha afectado más. Hay varias formas diferentes de demostrar esto, pero aquí hay una del registro de cambios:

const resolveDefered = $ .Deferred ()
resolveDefered.resolve ()
resolveDefered.then (() => console.log (“devolución de llamada”))
console.log (“después”)

Antes de 3.0, la salida sería callback / after porque cuando el controlador se agregaba en la línea 3, la lógica del método then() básicamente decía

  • Se agrega una devolución de llamada exitosa que debe ejecutarse cuando se resuelve el aplazado
  • El diferido ya está resuelto
  • Así que ejecútalo ahora

Esto viola la especificación A +. Lo que dice que todo lo que se adjunte después de que se resuelva la promesa debe ejecutarse de forma asíncrona en un pase diferente a través del bucle de eventos. Debe hacer el equivalente de setTimeout(() => fireAllUnresolved(), 0) para que todo el código síncrono se ejecute primero. Después de 3.0, esto hace lo correcto al iniciar sesión after / callback ya que el código en ejecución tiene garantizada la posibilidad de finalizar la ejecución antes de que cualquier controlador de promesas tenga la oportunidad de ejecutarse.

En mi experiencia, esto ha sido un gran problema en las pruebas unitarias donde cualquier cosa que use una promesa ahora siempre será asíncrona (y, por lo tanto, debe usar, por ejemplo, la devolución de llamada de mocha done ) en lugar de una mezcla extraña que depende de la implementación.