Problemas inherentes a jQuery $.Deferred (jQuery 1.x/2.x)

Resuelto Brian M. Hunt asked hace 10 años • 1 respuestas

@Domenic tiene un artículo muy completo sobre las fallas de los objetos diferidos de jQuery: Te estás perdiendo el punto de promesas . En él, Domenic destaca algunas fallas de las promesas de jQuery en comparación con otras, incluidas las promesas de Q , when.js, RSVP.js y ES6.

Me alejo del artículo de Domenic sintiendo que las promesas de jQuery tienen un defecto inherente, conceptualmente. Estoy tratando de poner ejemplos al concepto.

Supongo que hay dos preocupaciones con la implementación de jQuery:

1. El .thenmétodo no es encadenable

En otras palabras

promise.then(a).then(b)

jQuery llamará aentonces bcuando se promisecumpla.

Dado que .thendevuelve una nueva promesa en las otras bibliotecas de promesas, su equivalente sería:

promise.then(a)
promise.then(b)

2. El manejo de excepciones se incluye en jQuery.

La otra cuestión parecería ser el manejo de excepciones, a saber:

try {
  promise.then(a)
} catch (e) {
}

El equivalente en Q sería:

try {
  promise.then(a).done()
} catch (e) {
   // .done() re-throws any exceptions from a
}

En jQuery, la excepción se produce y aparece una burbuja cuando afalla en el bloque de captura. En las otras promesas, cualquier excepción ase trasladará a .doneu .catchotra captura asíncrona. Si ninguna de las llamadas API prometidas detecta la excepción, esta desaparece (de ahí la mejor práctica de Q de, por ejemplo, usar .donepara liberar cualquier excepción no controlada).

 

¿Los problemas anteriores cubren las preocupaciones con la implementación de promesas de jQuery, o he entendido mal o he omitido problemas?


Editar Esta pregunta se relaciona con jQuery <3.0; a partir de jQuery 3.0 alfa, jQuery cumple con Promises/A+.

Brian M. Hunt avatar May 20 '14 01:05 Brian M. Hunt
Aceptado

Actualización: jQuery 3.0 ha solucionado los problemas que se describen a continuación. Es verdaderamente compatible con Promises/A+.

Sí, las promesas de jQuery tienen problemas serios e inherentes.

Dicho esto, desde que se escribió el artículo, jQuery hizo esfuerzos significativos para ser más compatible con Promises/Aplus y ahora tienen un método .then que encadena.

Entonces, incluso en jQuery, returnsPromise().then(a).then(b)las funciones de devolución de promesas afuncionarán bcomo se esperaba, desencapsulando el valor de retorno antes de continuar. Como se ilustra en este violín :

function timeout(){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); },1000);
    return d.promise();
}

timeout().then(function(){
   document.body.innerHTML = "First";
   return timeout();
}).then(function(){
   document.body.innerHTML += "<br />Second";
   return timeout();
}).then(function(){
   document.body.innerHTML += "<br />Third";
   return timeout();
});

Sin embargo, los dos grandes problemas de jQuery son el manejo de errores y el orden de ejecución inesperado.

Manejo de errores

No hay forma de marcar una promesa de jQuery que se rechazó como "Manejada", incluso si la resuelve, a diferencia de catch. Esto hace que los rechazos en jQuery estén inherentemente rotos y sean muy difíciles de usar, nada como sincrónicos try/catch.

¿Puedes adivinar qué registros hay aquí? ( violín )

timeout().then(function(){
   throw new Error("Boo");
}).then(function(){
   console.log("Hello World");
},function(){
    console.log("In Error Handler");   
}).then(function(){
   console.log("This should have run");
}).fail(function(){
   console.log("But this does instead"); 
});

Si adivinaste "uncaught Error: boo"que estabas en lo cierto. Las promesas de jQuery no son seguras . No le permitirán manejar ningún error arrojado a diferencia de las promesas de Promises/Aplus. ¿Qué pasa con la seguridad del rechazo? ( violín )

timeout().then(function(){
   var d = $.Deferred(); d.reject();
   return d;
}).then(function(){
   console.log("Hello World");
},function(){
    console.log("In Error Handler");   
}).then(function(){
   console.log("This should have run");
}).fail(function(){
   console.log("But this does instead"); 
});

Los siguientes registros "In Error Handler" "But this does instead": no hay forma de manejar el rechazo de una promesa de jQuery. Esto es diferente al flujo que esperarías:

try{
   throw new Error("Hello World");
} catch(e){
   console.log("In Error handler");
}
console.log("This should have run");

¿Cuál es el flujo que se obtiene con las bibliotecas Promises/A+ como Bluebird y Q, y qué esperaría de su utilidad? Esto es enorme y la seguridad en el lanzamiento es un gran argumento de venta para las promesas. Aquí está Bluebird actuando correctamente en este caso .

Orden de ejecución

jQuery ejecutará la función pasada inmediatamente en lugar de posponerla si la promesa subyacente ya se resolvió, por lo que el código se comportará de manera diferente dependiendo de si la promesa a la que adjuntamos un controlador rechazada ya se resolvió. Esto libera efectivamente a Zalgo y puede causar algunos de los errores más dolorosos. Esto crea algunos de los errores más difíciles de depurar.

Si nos fijamos en el siguiente código: ( violín )

function timeout(){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); },1000);
    return d.promise();
}
console.log("This");
var p = timeout();
p.then(function(){
   console.log("expected from an async api.");
});
console.log("is");

setTimeout(function(){
    console.log("He");
    p.then(function(){
        console.log("̟̺̜̙͉Z̤̲̙̙͎̥̝A͎̣͔̙͘L̥̻̗̳̻̳̳͢G͉̖̯͓̞̩̦O̹̹̺!̙͈͎̞̬ *");
    });
    console.log("Comes");
},2000);

Podemos observar ese comportamiento tan peligroso, setTimeoutespera a que finalice el tiempo de espera original, por lo que jQuery cambia su orden de ejecución porque... ¿a quién le gustan las API deterministas que no causan desbordamientos de pila? Esta es la razón por la que la especificación Promises/A+ requiere que las promesas siempre se difieran hasta la siguiente ejecución del bucle de eventos.

Nota al margen

Vale la pena mencionar que las bibliotecas de promesas más nuevas y más sólidas como Bluebird (y experimentalmente When) no requieren .doneal final de la cadena como lo hace Q, ya que ellos mismos descubren los rechazos no controlados, también son mucho más rápidos que las promesas de jQuery o las promesas de Q.

Benjamin Gruenbaum avatar May 19 '2014 18:05 Benjamin Gruenbaum