web-dev-qa-db-fra.com

Problèmes inhérents à jQuery $ .Deferred (jQuery 1.x / 2.x)

@Domenic a un article très complet sur les défaillances des objets différés jQuery: Vous manquez le point de promesses . Domenic met en évidence quelques échecs des promesses jQuery par rapport à d'autres, y compris Q , when.js, RSVP.js et ES6 promesses.

Je m'éloigne de l'article de Domenic en pensant que les promesses jQuery ont un échec inhérent, conceptuellement. J'essaie de donner des exemples au concept.

Je suppose qu'il y a deux problèmes avec l'implémentation de jQuery:

1. Le .then la méthode n'est pas chaînable

En d'autres termes

promise.then(a).then(b)

jQuery appellera a puis b lorsque le promise sera rempli.

Puisque .then renvoie une nouvelle promesse dans les autres bibliothèques de promesses, leur équivalent serait:

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

2. La gestion des exceptions est diffusée dans jQuery.

L'autre problème semble être la gestion des exceptions, à savoir:

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

L'équivalent dans Q serait:

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

Dans jQuery, l'exception lance et fait des bulles lorsque a ne parvient pas au bloc catch. Dans les autres promesses, toute exception dans a serait appliquée à .done ou .catch ou autre capture asynchrone. Si aucun des appels d'API promesse n'attrape l'exception, il disparaît (d'où la meilleure pratique Q, par exemple d'utiliser .done pour libérer toutes les exceptions non gérées).

Les problèmes ci-dessus couvrent-ils les problèmes liés à la mise en œuvre de promesses jQuery, ou ai-je mal compris ou manqué des problèmes?


Modifier Cette question concerne jQuery <3.0; à partir de jQuery 3.0 alpha jQuery est conforme à Promises/A +.

45
Brian M. Hunt

Mise à jour: jQuery 3.0 a résolu les problèmes décrits ci-dessous. Il est vraiment conforme à Promises/A +.

Oui, les promesses jQuery ont des problèmes graves et inhérents.

Cela dit, depuis que l'article a été écrit, jQuery a fait des efforts importants pour être plus de plaintes Promesses/Aplus et ils ont maintenant une méthode .then qui enchaîne.

Ainsi, même dans jQuery returnsPromise().then(a).then(b) for promise, les fonctions de retour a et b fonctionneront comme prévu, déballant la valeur de retour avant de continuer. Comme illustré dans ce violon :

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

Cependant, les deux problèmes énorme avec jQuery sont la gestion des erreurs et l'ordre d'exécution inattendu.

La gestion des erreurs

Il n'y a aucun moyen de marquer une promesse jQuery rejetée comme "gérée", même si vous la résolvez, contrairement à catch. Cela rend les rejets dans jQuery intrinsèquement cassés et très difficiles à utiliser, rien de tel que try/catch Synchrone.

Pouvez-vous deviner quels journaux ici? ( violon )

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 vous avez deviné "uncaught Error: boo", Vous aviez raison. Les promesses de jQuery ne sont pas pas sûres . Ils ne vous laisseront pas gérer les erreurs levées contrairement aux promesses/promesses Aplus. Qu'en est-il du rejet de la sécurité? ( violon )

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

Les journaux suivants "In Error Handler" "But this does instead" - il n'y a aucun moyen de gérer le rejet d'une promesse jQuery. C'est différent du flux que vous attendez:

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

Quel est le flux que vous obtenez avec les bibliothèques Promises/A + comme Bluebird et Q, et ce que vous attendez de l'utilité. C'est énorme et la sécurité des lancers est un gros argument de vente pour les promesses. Voici Bluebird agissant correctement dans ce cas .

Ordre d'exécution

jQuery exécutera la fonction passée immédiatement plutôt que de la différer si la promesse sous-jacente est déjà résolue, donc le code se comportera différemment selon que la promesse à laquelle nous attachons un gestionnaire à rejeté est déjà résolue. C'est effectivement libérer Zalgo et peut provoquer certains des bugs les plus douloureux. Cela crée certains des bogues les plus difficiles à déboguer.

Si nous regardons le code suivant: ( violon )

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

Nous pouvons observer que ce comportement si dangereux, le setTimeout attend la fin du délai d'origine, donc jQuery change son ordre d'exécution parce que ... qui aime les API déterministes qui ne provoquent pas de débordements de pile? C'est pourquoi la spécification Promises/A + exige que les promesses soient toujours reportées à la prochaine exécution de la boucle d'événements.

Note latérale

Il convient de mentionner que les bibliothèques de promesses plus récentes et plus solides comme Bluebird (et expérimentalement quand) ne nécessitent pas .done À la fin de la chaîne comme le fait Q car elles comprennent elles-mêmes les rejets non gérés, elles sont également beaucoup plus rapides que jQuery promesses ou promesses Q.

52