Cet article popularise principalement l'utilisation des promesses.
Pendant longtemps, JavaScript a toujours géré de manière asynchrone sous la forme d'un rappel, et le mécanisme de rappel dans le domaine du développement frontal est presque profondément enraciné dans le cœur du peuple. Lors de la conception d'API, qu'il s'agisse d'un fabricant de navigateur, d'un développeur SDK ou d'auteur de diverses bibliothèques, ils suivent essentiellement la routine de rappel.
Ces dernières années, avec la maturité progressive du modèle de développement JavaScript, la spécification CommonJS est née, y compris la proposition de la spécification de promesse. Promise a complètement changé l'écriture de la programmation asynchrone JS, ce qui rend la programmation asynchrone très facile à comprendre.
Dans le modèle de rappel, nous supposons qu'une file d'attente asynchrone doit être exécutée, et le code peut ressembler à ceci:
loveMg ('a.jpg', function () {loadImg ('b.jpg', function () {loadImg ('c.jpg', function () {console.log ('All Done!');});});});C'est ce que nous appelons souvent la pyramide. Lorsqu'il existe de nombreuses tâches asynchrones, le maintien d'un grand nombre de rappels sera un désastre. De nos jours, Node.js est très populaire. Il semble que de nombreuses équipes veulent l'utiliser pour faire quelque chose pour s'entendre avec la "mode". Une fois discuté avec un camarade de classe opérationnel et maintenance, ils ont également prévu d'utiliser Node.js pour faire quelque chose, mais lorsqu'ils pensent aux couches de rappel de JS, ils sont découragés.
OK, le non-sens est terminé, passons au sujet.
Promise peut être familiarisé avec tout le monde, car la spécification des promesses est sortie depuis longtemps, et la promesse a été incluse dans ES6, et les versions supérieures des navigateurs Chrome et Firefox ont implémenté la promesse nativement, mais il y a moins d'API que la populaire bibliothèque de classe Promise de nos jours.
La soi-disant promesse peut être littéralement comprise comme une «promesse», ce qui signifie qu'un appelle B, B renvoie une «promesse» à A, puis a peut écrire ceci lors de l'écriture du plan: lorsque B me renvoie le résultat, un plan exécute S1. Au contraire, si B ne donne pas le résultat souhaité pour une raison quelconque, A exécute un plan d'urgence S2, afin que tous les risques potentiels se trouvent dans la plage contrôlable de A.
La phrase ci-dessus est traduite en code similaire à:
var resb = b (); var runa = function () {resb.phen (eecs1, eecs2);}; runa ();En regardant juste la ligne de code ci-dessus, il semble qu'il n'y ait rien de spécial. Mais la réalité peut être beaucoup plus compliquée que cela. Pour accomplir une chose, un peut s'appuyer sur la réponse de plus d'une personne B. Cela peut nécessiter de demander à plusieurs personnes en même temps, puis de mettre en œuvre le plan suivant après avoir reçu toutes les réponses. La traduction finale dans le code peut ressembler à ceci:
var resb = b (); var acc = c (); ... var runa = function () {reqb .then (resc, eecs2) .then (resd, eecs3) .then (rese, eecs4) ... .then (eecs1);}; runa ();Ici, différents mécanismes de traitement sont utilisés lorsque chaque réponse demandée qui ne correspond pas aux attentes. En fait, la spécification de promesse ne nécessite pas cela, et vous ne pouvez même rien faire (c'est-à-dire ne pas passer dans le deuxième paramètre de alors) ou le gérer uniformément.
Ok, apprenons à connaître les spécifications Promise / A +:
then (on peut dire que c'est alors le cœur de la promesse), puis doit retourner une promesse. Alors de la même promesse peut être appelé plusieurs fois, et l'ordre d'exécution des rappels est cohérent avec l'ordre lorsqu'ils sont définisComme vous pouvez le voir, il n'y a pas beaucoup de contenu dans la spécification de promesse, vous pouvez donc essayer de mettre en œuvre la promesse suivante.
Ce qui suit est une simple implémentation d'une promesse que j'ai référencé de nombreuses bibliothèques de promesses. Veuillez passer à la promesse dans le code.
Une brève analyse des idées:
La promesse du constructeur accepte un resolver de fonction, qui peut être compris comme passant une tâche asynchrone. Le résolveur accepte deux paramètres, l'un est un rappel lorsqu'il est réussi et l'autre est un rappel lors de l'échec. Ces deux paramètres sont égaux aux paramètres passés alors.
La seconde est la mise en œuvre de alors. Étant donné que la promesse exige que il faut ensuite retourner une promesse, une nouvelle promesse sera générée lors de l'appel, qui sera accrochée _next de la promesse actuelle. Plusieurs appels de la même promesse ne renverront que le _next précédemment généré.
Étant donné que les deux paramètres acceptés par la méthode alors sont facultatifs et qu'il n'y a aucune restriction sur le type, il peut être une fonction, une valeur spécifique ou une autre promesse. Voici la mise en œuvre spécifique de alors:
Promesse.prototype.then = function (résoudre, rejeter) {var next = this._next || (this._next = promesse ()); var status = this.status; var x; if ('en attente' === status) {isfn (résolution) && this._resolves.push (résolve); isfn (rejeter) && this._rejects.push (rejeter); retour ensuite; } if ('résolu' === statut) {if (! isfn (résolve)) {next.Resolve (résolve); } else {try {x = résolve (this.value); Resolvex (suivant, x); } catch (e) {this.reject (e); }} return suivant; } if ('rejeté' === status) {if (! isfn (rejeter)) {next.reject (rejeter); } else {try {x = rejeter (this.reason); Resolvex (suivant, x); } catch (e) {this.reject (e); }} return suivant; }};Ici, a alors simplifié la mise en œuvre d'autres bibliothèques de classe Promise, et l'implémentation est beaucoup plus complexe que cela, et il a également plus de fonctions. Par exemple, il existe un troisième paramètre - Notify, qui indique la progression actuelle de la promesse, qui est très utile lors du téléchargement de fichiers de conception, etc. Le traitement de divers paramètres de alors est la partie la plus compliquée. Les étudiants intéressés peuvent se référer à la mise en œuvre d'autres types de bibliothèques de promesses.
Sur la base de ce moment, au moins deux méthodes devraient être nécessaires, à savoir pour terminer la conversion de l'état de la promesse, en attente à résolu ou rejeté, et pour exécuter la file d'attente de rappel correspondante, à savoir resolve() et reject() des méthodes.
À ce stade, une simple promesse a été conçue. Voici des implémentations simples des deux fonctions promises suivantes:
fonction sleep (ms) {return function (v) {var p = promesse (); setTimeout (function () {p.Resolve (v);}, ms); Retour p; };}; fonction getImg (url) {var p = promesse (); var img = new image (); img.onload = function () {p.Resolve (this); }; img.onerror = fonction (err) {p.reject (err); }; img.url = url; retour p;}; Étant donné que le constructeur de promesses accepte une tâche asynchrone en tant que paramètre, getImg peut également être appelé comme ceci:
function getImg (url) {return promest (function (résolve, rejeter) {var img = new image (); img.onload = function () {Resolve (this);}; img.onerror = function (err) {rejeter (err);}; img.url = url;});};Ensuite (le moment de témoigner du miracle), supposons qu'il y ait une exigence BT pour implémenter ceci: obtenir une configuration JSON de manière asynchrone, analyser les données JSON et obtenir les images à l'intérieur, puis charger les images en séquence, et donner un effet de chargement lorsqu'aucune image n'est chargée.
fonction addImg (img) {$ ('# list'). find ('> li: dernier-enfant'). html (''). append (img);}; fonction prend () {$ ('<li>') .html ('Loading ...') .APPENDTO ($ ('# list'));}; function run () {$ ('# cait'). getData ('map.json') .then (function (data) {$ ('h4'). html (data.name); return data.list.reduce (function (promest, item) {return promest .then (prend) .then (sleep (1000)) .then (function () {return getImg (item.Url);}). Promesse.resolve ());}) .Then (Sleep (300)) .Then (function () {$ ('# Done'). Show ();});}; $ ('# run'). Sur ('click', run);Le sommeil ici est juste ajouté pour voir l'effet, vous pouvez cliquer pour voir la démo! Bien sûr, l'exemple Node.js peut être consulté ici.
Ici, la méthode statique de Promise.resolve(v) renvoie simplement une promesse avec V comme résultat positif. V ne peut pas être transmis, ou il peut être une fonction ou un objet ou une fonction contenant then la méthode (c'est-à-dire que celle-ci).
Des méthodes statiques similaires incluent Promise.cast(promise) , qui génère une promesse avec promesse comme résultat positif;
Promise.reject(reason) génère une promesse avec raison comme résultat négatif.
Nos scénarios d'utilisation réels peuvent être très complexes et nécessitent souvent de multiples tâches asynchrones pour être exécutées parallèles, parallèles ou en série. À l'heure actuelle, vous pouvez faire diverses extensions à promettre, telles que la mise en œuvre Promise.all() , en acceptant la file d'attente de promesses et en attendant qu'ils se terminent avant de continuer, et par exemple, Promise.any() , lorsque l'une des files d'attente de promesses se trouvera dans l'état d'achèvement, la prochaine opération sera déclenchée.
Vous pouvez vous référer à cet article dans HTML5Rocks JavaScript Promises. Actuellement, des navigateurs avancés tels que Chrome et Firefox ont des objets Promise intégrés, fournissant plus d'interfaces de fonctionnement, telles que Promise.all() , qui prend en charge le passage dans un tableau de promesses, puis exécute lorsque toutes les promesses sont terminées. Il y a aussi une capture d'exception plus amicale et puissante, qui devrait être suffisante pour faire face à la programmation asynchrone quotidienne.
Les bibliothèques JS populaires ont de nos jours qui ont presque toutes mis en œuvre une promesse à des degrés divers, telles que Dojo, JQuery, Zepto, When.js, Q, etc., mais la plupart des objets exposés Deferred . En prenant jQuery (zepto similaire) comme exemple, implémentez le getImg() ci-dessus:
fonction getImg (url) {var def = $ .deferred (); var img = new image (); img.onload = function () {def.Resolve (this); }; img.onerror = fonction (err) {def.reject (err); }; img.src = url; return def.promise ();}; Bien sûr, dans JQuery, de nombreuses opérations reviennent différées ou prometteurs, comme animate et ajax :
// ajax $ .ajax (opacité ': 0}, 1000) .promise () .then (function () {console.log (' Done ');}); // ajax $. $ .ajax (Options2)) .Then (function () {console.log ('All Done.');}, function () {console.error ('Il y a quelque chose de mal.');}); JQuery implémente également done() et fail() , qui sont en fait des raccourcis de la méthode alors.
Pour gérer les files d'attente de promesses, jQuery implémente $.when() , et son utilisation est similaire à Promise.all() .
Pour d'autres bibliothèques de classe, il convient de mentionner ici que lorsque.js a peu de code, il implémente entièrement la promesse, prend en charge le navigateur et le node.js, et fournit des API plus riches, ce qui est un bon choix. En raison des limitations de l'espace, nous ne l'élargirons plus.
Nous voyons que peu importe la complexité de la mise en œuvre de la promesse, son utilisation est très simple et le code organisationnel est très clair. À partir de maintenant, il n'est pas nécessaire d'être torturé par rappel.
Enfin, la promesse est si élégante! Mais Promise ne résout que le problème de la nidification profonde des rappels. C'est le générateur qui simplifie vraiment la programmation asynchrone JavaScript. Du côté Node.js, il est recommandé de considérer le générateur.
Article suivant, générateur d'étude.
Texte original de GitHub: https://github.com/chemdemo/chemdemo.github.io/issues/6