jquery - example - promise js
Problemas inherentes a jQuery $.Deferred(jQuery 1.x/2.x) (1)
Actualización: jQuery 3.0 ha solucionado los problemas descritos a continuación. Es verdaderamente compatible con Promises / A +.
Sí, las promesas de jQuery tienen problemas graves e inherentes.
Dicho esto, desde que el artículo fue escrito jQuery hizo esfuerzos significativos para ser más quejas de Promises / Aplus y ahora tienen un método .then que encadena.
Así que incluso en jQuery returnsPromise().then(a).then(b)
para las funciones returnsPromise().then(a).then(b)
a
y b
funcionarán como se esperaba, desenvolver el valor devuelto antes de continuar hacia adelante. Como se ilustra en este fiddle :
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 con 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 "Controlada", incluso si la resuelve, a diferencia de la captura. Esto hace que los rechazos en jQuery sean intrínsecamente rotos y muy difíciles de usar, nada parecido a try/catch
sincrónico.
¿Puedes adivinar qué registros aquí? ( fiddle )
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 adivinó "uncaught Error: boo"
, estaba en lo cierto. Las promesas de jQuery no son seguras . No te permitirán manejar ningún error lanzado, a diferencia de lo que promete Promises / Aplus. ¿Qué hay de rechazar la seguridad? ( fiddle )
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 manera de manejar un rechazo de 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 obtienes con bibliotecas Promises / A + como Bluebird y Q, y qué esperarías de utilidad? Esto es enorme y la seguridad de tiro es un gran punto de venta para las promesas. Aquí está Bluebird actuando correctamente en este caso .
Orden de ejecución
jQuery ejecutará la función pasada de forma inmediata en lugar de diferirla 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 estamos adjuntando un controlador rechazado ya se resolvió. Esto libera Zalgo y puede causar algunos de los errores más dolorosos. Esto crea algunos de los errores más difíciles de depurar.
Si miramos el siguiente código: ( fiddle )
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, el setTimeout
espera a que setTimeout
el tiempo de espera original, por lo que jQuery cambia su orden de ejecución porque ... ¿a quién le gustan las API determinísticas que no causan desbordamientos de pila? Esta es la razón por la cual la especificación Promises / A + requiere que las promesas sean siempre diferidas a la siguiente ejecución del ciclo de eventos.
Nota al margen
Vale la pena mencionar que las bibliotecas promesas más nuevas y más fuertes como Bluebird (y experimentalmente When) no requieren .done
al final de la cadena como lo hace Q ya que ellos mismos resuelven los rechazos no controlados, también son mucho más rápidos que jQuery promesas o Q promesas .
@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 resalta algunas fallas de las promesas de jQuery en comparación con otras que incluyen 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 método .then no es encadenable
En otras palabras
promise.then(a).then(b)
jQuery llamará a a
luego b
cuando se cumpla la promise
.
Dado que. .then
devuelve una nueva promesa en las otras bibliotecas de promesa, su equivalente sería:
promise.then(a)
promise.then(b)
2. El manejo de excepciones se borra en jQuery.
El otro problema parece 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 lanza y burbujea cuando falla el bloque catch. En las otras promesas, cualquier excepción en a
se llevaría a la .done
o .catch
u otra captura asincrónica. Si ninguna de las llamadas API promete atrapar la excepción, desaparece (de ahí la mejor práctica Q de, por ejemplo, usar .done
para liberar cualquier excepción no controlada).
¿Los problemas anteriores cubren las preocupaciones con la implementación de jQuery de las promesas, o he entendido mal o he perdido problemas?
Editar Esta pregunta se relaciona con jQuery <3.0; a partir de jQuery 3.0 alpha jQuery es compatible con Promises / A +.