Quelles sont les différences entre différé, promis et futur en JavaScript?


300

Quelles sont les différences entre les reports, les promesses et les contrats à terme?
Y a-t-il une théorie généralement approuvée derrière ces trois éléments?


11
Je ne pense pas que cela ait quoi que ce soit à voir avec jQuery ...
BoltClock


8
Je ne les ai pas utilisés moi-même mais voici une très bonne introduction sur wikipedia en.wikipedia.org/wiki/Futures_and_promises . Bien que je ne comprenne pas correctement le cas d'utilisation. Dans un langage asynchrone piloté par événements comme javascript. À première vue, je ne vois pas ce qu'ils offrent sur les rappels, à part peut-être une API plus propre. J'aimerais que quelqu'un fournisse un exemple d'utilisation et montre comment ces concepts sont appliqués, et pourquoi les rappels seraient une solution inefficace. @duri cela n'a rien à voir avec jQuery. La balise jQuery peut-elle être supprimée s'il vous plaît
AshHeskes

2
@ jfriend00 excellent lien, devrait probablement être intégré dans une réponse.
fncomp

Réponses:


97

À la lumière de l'aversion apparente pour la façon dont j'ai essayé de répondre à la question du PO. La réponse littérale est, une promesse est quelque chose de partagé avec d'autres objets, tandis qu'un différé doit rester privé. Principalement, un différé (qui prolonge généralement la promesse) peut se résoudre, tandis qu'une promesse pourrait ne pas être en mesure de le faire.

Si les détails vous intéressent, examinez Promesses / A + .


Pour autant que je sache, le but principal est d'améliorer la clarté et de desserrer le couplage via une interface standardisée. Voir la lecture suggérée de @ jfriend00:

Plutôt que de transmettre directement des rappels à des fonctions, ce qui peut conduire à des interfaces étroitement couplées, l'utilisation de promesses permet de séparer les problèmes de code synchrone ou asynchrone.

Personnellement, j'ai trouvé le report particulièrement utile lorsqu'il s'agit, par exemple, de modèles remplis de demandes asynchrones, du chargement de scripts dotés de réseaux de dépendances et de la rétroaction des utilisateurs pour former des données de manière non bloquante.

En effet, comparez la forme de rappel pur de faire quelque chose après avoir chargé CodeMirror en mode JS de manière asynchrone (excuses, je n'ai pas utilisé jQuery depuis un moment ):

/* assume getScript has signature like: function (path, callback, context) 
   and listens to onload && onreadystatechange */
$(function () {
   getScript('path/to/CodeMirror', getJSMode);

   // onreadystate is not reliable for callback args.
   function getJSMode() {
       getScript('path/to/CodeMirror/mode/javascript/javascript.js', 
           ourAwesomeScript);
   };

   function ourAwesomeScript() {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   };
});

Pour la version formulée des promesses (encore une fois, excuses, je ne suis pas à jour sur jQuery):

/* Assume getScript returns a promise object */
$(function () {
   $.when(
       getScript('path/to/CodeMirror'),
       getScript('path/to/CodeMirror/mode/javascript/javascript.js')
   ).then(function () {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   });
});

Toutes mes excuses pour le semi-pseudo-code, mais j'espère que cela rend l'idée centrale quelque peu claire. Fondamentalement, en renvoyant une promesse standardisée, vous pouvez passer la promesse, permettant ainsi un regroupement plus clair.


10
Bien que cette réponse puisse être utile, elle ne répond pas en fait à la question: les soi-disant différés sont soit des contrats à terme, soit des promesses, selon la mise en œuvre.
awdz9nld

@ MartinKällman Vous avez raison! Je n'avais pas revu cela depuis un moment et j'ai appris un peu. Je posterai une réponse séparée ci-dessous, mais laissez cela car les gens semblent avoir profité de l'exemple d'utilisation.
fncomp

@ MartinKällman, a envisagé d'écrire une nouvelle réponse. Cependant, je pense que le PO voulait réellement savoir à quoi servent les promesses et les reports. La réponse à sa véritable question serait, en gros, "les différés peuvent se résoudre eux-mêmes. AFAIK, la théorie derrière les promesses et les différés vient de [Functional Reactive Programming | haskell.org/haskellwiki/Functional_Reactive_Programming] , qui est une technique pour aplatir les rappels. . "
fncomp

2
C'est tout simplement faux et vos exemples sont tout aussi faciles à faire avec les rappels. Les promesses ne concernent pas l'agrégation de rappel et le découplage, mais la fourniture d'une DSL pour écrire du code asynchrone comme le code de synchronisation est écrite. Surtout fn(callback, errback)n'est pas plus étroitement couplé ou moins utile que fn().then(callback, errback)- mais c'est une mauvaise façon d'utiliser les promesses de toute façon. Je déteste particulièrement l' $.whenexemple du culte du fret - il n'y a absolument aucune raison pour laquelle vous ne pouvez pas avoir une $.whenfonction qui fonctionne avec les rappels.
Esailija

Cela ne répond pas à la question +1 que je pourrais être en mesure de savoir ce qu'est l'enfer de rappel.
Bhojendra Rauniyar

146

Ces réponses, y compris la réponse sélectionnée, sont bonnes pour introduire des promesses conceptuellement, mais manquent de détails précis sur les différences de terminologie qui surviennent lors de l'utilisation de bibliothèques les implémentant (et il existe des différences importantes).

Comme il s'agit toujours d' une spécification en évolution , la réponse vient actuellement de la tentative de sonder à la fois les références (comme wikipedia ) et les implémentations (comme jQuery ):

  • Différé : Jamais décrit dans les références populaires, 1 2 3 4 mais couramment utilisé par les implémentations comme arbitre de la résolution des promesses (implémentation et ). 5 6 7 resolvereject

    Parfois, les différés sont également des promesses (mise en œuvre then), 5 6 d' autres fois, il est considéré comme plus pur d'avoir le différé uniquement capable de résolution, et forçant l'utilisateur à accéder à la promesse d'utilisation . 7 then

  • Promesse : le mot le plus complet pour la stratégie en discussion.

    Un objet proxy stockant le résultat d'une fonction cible dont nous aimerions résumer la synchronicité, plus exposant une thenfonction acceptant une autre fonction cible et renvoyant une nouvelle promesse. 2

    Exemple de CommonJS :

    > asyncComputeTheAnswerToEverything()
        .then(addTwo)
        .then(printResult);
    44

     

    Toujours décrit dans les références populaires, mais jamais précisé à qui incombe la résolution de la responsabilité. 1 2 3 4

    Toujours présent dans les implémentations populaires et jamais doté de capacités de résolution. 5 6 7

  • Avenir : un terme apparemment déprécié trouvé dans certaines références populaires 1 et au moins une implémentation populaire, 8 mais apparemment retiré de la discussion de préférence pour le terme `` promesse '' 3 et pas toujours mentionné dans les introductions populaires au sujet. 9

    Cependant, au moins une bibliothèque utilise le terme de manière générique pour abstraire la synchronisation et la gestion des erreurs, sans fournir de thenfonctionnalité. 10 Il n'est pas clair si l'évitement du terme «promesse» était intentionnel, mais c'est probablement un bon choix puisque les promesses sont construites autour de «mots». 2

Références

  1. Wikipedia sur Promises & Futures
  2. Promesses / A + spec
  3. Norme DOM sur les promesses
  4. DOM Standard Promises Spec WIP
  5. Report de la boîte à outils DOJO
  6. jQuery différés
  7. Q
  8. FutureJS
  9. Section Javascript fonctionnelle sur les promesses
  10. Futures dans les tests d'intégration AngularJS

Diverses choses potentiellement déroutantes


5
Pour ajouter un peu plus de précision sur le terme "avenir" - les futurs ont une longue histoire dans de nombreux langages de programmation remontant au milieu des années 80. Et le terme est encore largement utilisé aujourd'hui, notamment sur la JVM. JavaScript semble avoir choisi d'utiliser le terme "Promise" pour signifier quelque chose de similaire à ce que Java entend par "Future". Scala sépare le même concept en un "avenir" et une "promesse" pour faire référence au descripteur "lecture" et au descripteur "écriture" de ce que les programmeurs JavaScript appellent une promesse.
Heather Miller,

1
Et bien sûr, Microsoft a dû trouver leur propre terme, donc en C #, ils s'appellentTask
BlueRaja - Danny Pflughoeft

72

Ce qui m'a vraiment plu, c'est cette présentation de Domenic Denicola.

Dans un github gistub , il a donné la description que j'aime le plus, c'est très concis:

Le point de promesses est de nous rendre la composition fonctionnelle et le bullage d'erreur dans le monde asynchrone.

En d'autres termes, les promesses sont un moyen qui nous permet d'écrire du code asynchrone presque aussi facile à écrire que s'il était synchrone .

Considérez cet exemple, avec des promesses:

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

Cela fonctionne comme si vous écriviez ce code synchrone:

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

(Si cela semble encore compliqué, regardez cette présentation!)

En ce qui concerne différé, c'est un moyen .resolve()ou des .reject()promesses. Dans la spécification Promises / B , il est appelé .defer(). Dans jQuery, c'est $.Deferred().

Veuillez noter que, pour autant que je sache, l'implémentation de Promise dans jQuery est cassée (voir cet essentiel), au moins à partir de jQuery 1.8.2.
Il implémente supposément les supports Promises / A , mais vous n'obtenez pas le traitement d'erreur correct que vous devriez, dans le sens où l'ensemble de la fonctionnalité "async try / catch" ne fonctionnera pas. Ce qui est dommage, car avoir un "try / catch" avec du code asynchrone est vraiment cool.

Si vous allez utiliser Promises (vous devriez les essayer avec votre propre code!), Utilisez Q de Kris Kowal . La version jQuery n'est qu'un agrégateur de rappel pour écrire du code jQuery plus propre, mais elle manque le point.

En ce qui concerne l'avenir, je n'en ai aucune idée, je n'ai vu cela dans aucune API.

Edit: le discours de Domenic Denicola sur YouTube sur Promises du commentaire de @Farm ci-dessous.

Une citation de Michael Jackson (oui, Michael Jackson ) de la vidéo:

Je veux que vous brûliez cette phrase dans votre esprit: une promesse est une valeur asynchrone .

C'est une excellente description: une promesse est comme une variable du futur - une référence de première classe à quelque chose qui, à un moment donné, existera (ou arrivera).


5
Une grande explication de Futures (maintenant implémentée dans le DOM!) Par un membre de l'équipe centrale de W3 et Chrome se trouve ici: xanthir.com/b4PY0
oligofren

1
@oligofren Merci pour le lien, ça semble sympa! Au fait, quel favicon mystérieusement ennuyeux lol.
Camilo Martin

1
Cette réponse a besoin de beaucoup plus de votes positifs. Il devrait être voté plus haut que la réponse acceptée OMI.
Chev

1
Conférence YouTube de Domenic Denicola sur Promises: youtube.com/watch?v=hf1T_AONQJU
Ferme du

@Farm Great! J'ajouterai cela à la réponse.
Camilo Martin

32

Une promesse représente un proxy pour une valeur qui n'est pas nécessairement connue lorsque la promesse est créée. Il vous permet d'associer des gestionnaires à la valeur de réussite éventuelle d'une action asynchrone ou à la raison de l'échec. Cela permet aux méthodes asynchrones de renvoyer des valeurs comme les méthodes synchrones: au lieu de la valeur finale, la méthode asynchrone renvoie une promesse d'avoir une valeur à un moment donné dans le futur.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

La deferred.promise()méthode permet à une fonction asynchrone d'empêcher un autre code d'interférer avec la progression ou l'état de sa requête interne. La promesse expose uniquement les méthodes différées nécessaires pour attacher des gestionnaires supplémentaires ou déterminer l'état ( puis, terminé, échouer, toujours, rediriger, progression, état et promesse ), mais pas celles qui changent l'état ( résoudre, rejeter, notifier, résoudre, avec, rejeterAvec et notifierAvec ).

Si la cible est fournie, deferred.promise()y attachera les méthodes, puis retournera cet objet plutôt que d'en créer un nouveau. Cela peut être utile pour associer le comportement Promise à un objet qui existe déjà.

Si vous créez un différé, conservez une référence au différé afin qu'il puisse être résolu ou rejeté à un moment donné. Renvoyez uniquement l'objet Promise via deferred.promise () afin que d'autres codes puissent enregistrer des rappels ou inspecter l'état actuel.

Simplement, nous pouvons dire qu'une promesse représente une valeur qui n'est pas encore connue alors que différée représente un travail qui n'est pas encore terminé.


entrez la description de l'image ici


1
plus 1 pour la représentation graphique. Bravisimo !! ^ _ ^
Ashok MA

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.