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.
Alors quelle est l'idée principale?
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:
- Vous n'avez pas besoin de connaître les rappels qui utiliseront la valeur avant la fin de la tâche.
- Au lieu d'attendre des rappels comme arguments de vos fonctions, vous pouvez facilement
return
un objet Promise
- La promesse stockera la valeur et vous pourrez ajouter un rappel de manière transparente à tout moment. Il sera appelé lorsque le résultat sera disponible. La "transparence" implique que lorsque vous avez une promesse et y ajoutez un rappel, cela ne fait aucune différence pour votre code si le résultat est déjà arrivé - l'API et les contrats sont les mêmes, ce qui simplifie beaucoup la mise en cache / mémoisation.
- Vous pouvez facilement ajouter plusieurs rappels
Les promesses sont chaînables ( monadiques , si vous voulez ):
- Si vous devez transformer la valeur que représente une promesse, vous mappez une fonction de transformation sur la promesse et récupérez une nouvelle promesse qui représente le résultat transformé. Vous ne pouvez pas obtenir la valeur de manière synchrone pour l'utiliser d'une manière ou d'une autre, mais vous pouvez facilement lever la transformation dans le contexte de la promesse. Pas de rappels standard.
- Si vous souhaitez chaîner deux tâches asynchrones, vous pouvez utiliser la
.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.
var p1 = api1(); // returning a promise
var p3 = p1.then(function(api1Result) {
var p2 = api2(); // returning a promise
return p2; // The result of p2 …
}); // … becomes the result of p3
// So it does not make a difference whether you write
api1().then(function(api1Result) {
return api2().then(console.log)
})
// or the flattened version
api1().then(function(api1Result) {
return api2();
}).then(console.log)
L'aplatissement ne vient pas comme par magie, mais vous pouvez facilement le faire. Pour votre exemple fortement imbriqué, l'équivalent (presque) serait
api1().then(api2).then(api3).then(/* do-work-callback */);
Si voir le code de ces méthodes aide à comprendre, voici une lib de promesse la plus basique en quelques lignes .
Quel est le gros problème des promesses?
L'abstraction Promise permet une bien meilleure composabilité des fonctions. Par exemple, à côté then
du chaînage, la all
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.
Sans parler de devoir convertir les choses en promesses.
C'est assez banal en fait avec de bonnes bibliothèques de promesses, voir Comment convertir une API de rappel existante en promesses?