Je développe JavaScript depuis quelques années et je ne comprends pas du tout les histoires de promesses.
Il semble que tout ce que je fais c'est changer:
api(function(result){
api2(function(result2){
api3(function(result3){
// do work
});
});
});
Pour lequel je pourrais utiliser une bibliothèque comme async de toute façon, avec quelque chose comme:
api().then(function(result){
api2().then(function(result2){
api3().then(function(result3){
// do work
});
});
});
Ce qui est plus de code et moins lisible. Je n'ai rien gagné ici, ce n'est pas soudainement «plat» comme par magie. Sans parler de devoir convertir les choses en promesses.
Alors, quel est le gros problème des promesses ici?
Réponses:
Les promesses ne sont pas des rappels. Une promesse représente le résultat futur d'une opération asynchrone . Bien sûr, en les écrivant comme vous le faites, vous obtenez peu d'avantages. Mais si vous les écrivez de la façon dont ils sont censés être utilisés, vous pouvez écrire du code asynchrone d'une manière qui ressemble au code synchrone et est beaucoup plus facile à suivre:
Certes, pas beaucoup moins de code, mais beaucoup plus lisible.
Mais ce n'est pas la fin. Découvrons les véritables avantages: que se passe-t-il si vous souhaitez vérifier toute erreur dans l'une des étapes? Ce serait l'enfer de le faire avec des rappels, mais avec des promesses, c'est du gâteau:
À peu près la même chose qu'un
try { ... } catch
bloc.Encore mieux:
Et mieux encore: Et si ces 3 appels
api
,api2
,api3
pourraient exécuter simultanément (par exemple , si elles étaient des appels AJAX) mais vous besoin d'attendre les trois? Sans promesses, vous devriez avoir à créer une sorte de compteur. Avec des promesses, en utilisant la notation ES6, c'est un autre morceau de gâteau et assez soigné:J'espère que vous voyez les promesses sous un nouveau jour maintenant.
la source
api2
etapi3
? la dernière.then
ne serait-elle appelée qu'une fois ces opérations asynchrones terminées?Oui, les promesses sont des rappels asynchrones. Ils ne peuvent rien faire que les rappels ne puissent pas faire, et vous rencontrez les mêmes problèmes avec l'asynchronie qu'avec les rappels simples.
Cependant, les promesses sont plus que de simples rappels. Ils sont une abstraction très puissante, permettent un code fonctionnel plus propre et meilleur avec un passe-partout moins sujet aux erreurs.
Les promesses sont des objets représentant le résultat d'un seul calcul (asynchrone). Ils ne résolvent ce résultat qu'une seule fois. Il y a quelques choses ce que cela signifie:
Les promesses mettent en œuvre un modèle d'observation:
return
un objet PromiseLes promesses sont chaînables ( monadiques , si vous voulez ):
.then()
méthode. Il faudra un rappel pour être appelé avec le premier résultat et retourne une promesse pour le résultat de la promesse que le rappel renvoie.Cela semble compliqué? Temps pour un exemple de code.
L'aplatissement ne vient pas comme par magie, mais vous pouvez facilement le faire. Pour votre exemple fortement imbriqué, l'équivalent (presque) serait
Si voir le code de ces méthodes aide à comprendre, voici une lib de promesse la plus basique en quelques lignes .
L'abstraction Promise permet une bien meilleure composabilité des fonctions. Par exemple, à côté
then
du chaînage, laall
fonction crée une promesse pour le résultat combiné de plusieurs promesses d'attente parallèle.Enfin et surtout, les promesses sont livrées avec une gestion intégrée des erreurs. Le résultat du calcul peut être que la promesse est remplie d'une valeur ou qu'elle est rejetée avec une raison. Toutes les fonctions de composition gèrent cela automatiquement et propagent les erreurs dans les chaînes de promesses, de sorte que vous n'avez pas besoin de vous en soucier explicitement partout - contrairement à une implémentation de rappel simple. Au final, vous pouvez ajouter un rappel d'erreur dédié pour toutes les exceptions survenues.
C'est assez banal en fait avec de bonnes bibliothèques de promesses, voir Comment convertir une API de rappel existante en promesses?
la source
.then(console.log)
, car console.log dépend du contexte de la console. De cette façon, cela provoquera une erreur d'invocation illégale. Utilisezconsole.log.bind(console)
oux => console.log(x)
pour lier le contexte.console
méthodes sont déjà liées. Et bien sûr, j'ai seulement dit que les deux nidifications ont exactement le même comportement, pas qu'elles fonctionneraient :-POutre les réponses déjà établies, avec des fonctions de ES6 Promesses tourner d'un brillant modestement petit nain bleu droit dans une géante rouge. C'est sur le point de s'effondrer dans une supernova:
Comme l'a souligné oligofren , sans arguments entre les appels API, vous n'avez pas du tout besoin des fonctions d'encapsulation anonyme:
Et enfin, si vous voulez atteindre un niveau de trou noir supermassif, des promesses peuvent être attendues:
la source
apiX
méthodes, vous pourriez aussi bien ignorer complètement les fonctions de direction:api().then(api2).then(api3).then(r3 => console.log(r3))
.En plus des réponses impressionnantes ci-dessus, 2 points supplémentaires peuvent être ajoutés:
1. Différence sémantique:
Les promesses peuvent être déjà résolues lors de la création. Cela signifie qu'ils garantissent des conditions plutôt que des événements . S'ils sont déjà résolus, la fonction résolue qui lui est transmise est toujours appelée.
Inversement, les rappels gèrent les événements. Par conséquent, si l'événement qui vous intéresse s'est produit avant l'enregistrement du rappel, le rappel n'est pas appelé.
2. Inversion du contrôle
Les rappels impliquent une inversion de contrôle. Lorsque vous enregistrez une fonction de rappel avec une API, le runtime Javascript stocke la fonction de rappel et l'appelle à partir de la boucle d'événements une fois qu'elle est prête à être exécutée.
Reportez-vous à la boucle d'événement Javascript pour une explication.
Avec Promises , le contrôle réside dans le programme appelant. La méthode .then () peut être appelée à tout moment si nous stockons l'objet promise.
la source
En plus des autres réponses, la syntaxe ES2015 se fond parfaitement avec les promesses, réduisant encore plus le code standard:
la source
Les promesses ne sont pas des rappels, les deux sont des idiomes de programmation qui facilitent la programmation asynchrone. L'utilisation d'un style de programmation asynchrone / attente à l'aide de coroutines ou de générateurs qui renvoient des promesses pourrait être considérée comme un troisième idiome de ce type. Une comparaison de ces idiomes à travers différents langages de programmation (y compris Javascript) est ici: https://github.com/KjellSchubert/promise-future-task
la source
Non pas du tout.
Les rappels sont simplement des fonctions en JavaScript qui doivent être appelées puis exécutées une fois l'exécution d'une autre fonction terminée. Alors comment ça se passe?
En fait, en JavaScript, les fonctions sont elles-mêmes considérées comme des objets et donc comme tous les autres objets, même les fonctions peuvent être envoyées comme arguments à d'autres fonctions . Le cas d'utilisation le plus courant et le plus générique auquel on puisse penser est la fonction setTimeout () en JavaScript.
Les promesses ne sont rien d'autre qu'une approche beaucoup plus improvisée de la gestion et de la structuration du code asynchrone par rapport à la même chose avec les rappels.
La promesse reçoit deux rappels dans la fonction constructeur: résoudre et rejeter. Ces rappels à l'intérieur des promesses nous fournissent un contrôle précis sur la gestion des erreurs et les cas de réussite. Le rappel de résolution est utilisé lorsque l'exécution de la promesse a réussi et le rappel de rejet est utilisé pour gérer les cas d'erreur.
la source
Aucune promesse ne se limite aux rappels
exemple Vous pouvez utiliser des promesses natives javascript avec le noeud js
la source
Les promesses JavaScript utilisent en fait des fonctions de rappel pour déterminer quoi faire après la résolution ou le rejet d'une promesse, les deux ne sont donc pas fondamentalement différentes. L'idée principale de Promises est de prendre des rappels - en particulier des rappels imbriqués où vous souhaitez effectuer une sorte d'actions, mais ce serait plus lisible.
la source
Aperçu des promesses:
Dans JS, nous pouvons encapsuler des opérations asynchrones (par exemple, appels de base de données, appels AJAX) en promesses. Habituellement, nous voulons exécuter une logique supplémentaire sur les données récupérées. JS promet d'avoir des fonctions de gestionnaire qui traitent le résultat des opérations asynchrones. Les fonctions de gestionnaire peuvent même contenir d'autres opérations asynchrones qui peuvent dépendre de la valeur des opérations asynchrones précédentes.
Une promesse a toujours les 3 états suivants:
Une promesse en attente peut être résolue / remplie ou rejetée avec une valeur. Ensuite, les méthodes de gestionnaire suivantes qui prennent des rappels comme arguments sont appelées:
Promise.prototype.then()
: Lorsque la promesse est résolue, l'argument de rappel de cette fonction sera appelé.Promise.prototype.catch()
: Lorsque la promesse est rejetée, l'argument de rappel de cette fonction sera appelé.Bien que les compétences de méthodes ci-dessus obtiennent des arguments de rappel, elles sont bien supérieures à l'utilisation de seuls rappels, voici un exemple qui clarifiera beaucoup:
Exemple
then
méthode est appelée et la valeur résolue est transmise en tant qu'argument du rappelcatch
méthode est appelée et la valeur rejetée est transmise en argumentcatch
etthen
retournent des promesses, c'est pourquoi nous pouvons les enchaîner. Ils encapsulent toute valeur retournéePromise.resolve
et toute valeur renvoyée (à l'aide duthrow
mot clé)Promise.reject
. Donc, toute valeur retournée est transformée en promesse et sur cette promesse, nous pouvons à nouveau appeler une fonction de gestionnaire.catch
méthode gère toutes les erreurs qui se sont produites avant lecatch
gestionnaire.la source