Maison >interface Web >js tutoriel >Compréhension approfondie des promesses en JS

Compréhension approfondie des promesses en JS

yulia
yuliaoriginal
2018-09-10 14:52:321433parcourir

Lors de l'utilisation de Promise, notre compréhension et notre utilisation les plus simples sont de fournir le résultat asynchrone à résoudre en tant que paramètre comme le code ci-dessus, puis de transmettre une fonction personnalisée comme fonction de traitement des résultats à la méthode then. Mais que sont exactement les deux paramètres résoudre et rejeter ? En coulisses, quelle est sa méthode de travail fondamentale ? Examinons-le d’un point de vue normatif.

new Promise((resolve, reject) => setTimeout(resolve, 1000, 'foo'))  
 .then(console.log)  
 // foo (1s后)

TL;DR
1. Le mécanisme de fonctionnement de la promesse est similaire au rappel, qui utilisent tous deux une opération abstraite interne Job pour implémenter un
2 in asynchrone. Constructeur de promesse Les fonctions de résolution/rejet sont créées en interne et le paramètre transmis lors de leur appel est le résultat à analyser, qui est inséré dans la file d'attente des travaux avec la fonction de traitement transmise par l'utilisateur et stockée dans la promesse. . Le paramètre transmis peut également être une promesse, utilisée en interne dans Promise.all/race.
3. Promise.prototype.puis détermine en fonction de l'état actuel de la promesse s'il faut immédiatement retirer le résultat stocké dans la promesse et l'insérer directement dans la file d'attente des tâches avec la fonction de traitement dans le paramètre, ou l'associer à la promesse en premier et la traiter en conséquence. puis appellera implicitement la fonction de construction Promise pour construire une nouvelle promesse et la renvoyer.
4. Promise.all crée d'abord une nouvelle promesse, puis initialise un tableau de résultats vide et un compteur pour compter les promesses résolues, puis itère, et pour chaque valeur d'itération, il créera une promesse et définira alors la promesse sur ajoutez le résultat et le compteur au tableau de résultats - le résultat final sera résolu lorsque le compteur diminuera à 0.
5. Promise.race créera également une nouvelle promesse principale. Ensuite, principalement sur la base de la restriction selon laquelle la promesse ne peut être résolue qu'une seule fois, une autre promesse sera créée pour chaque valeur d'itération. Celle qui est résolue en premier sera résolue. par la promesse principale, solve renvoie d'abord le résultat.

new Promise(executor)

Tout d'abord, commençons par le constructeur Promise C'est la valeur de l'attribut Promise de l'objet global. dans l'environnement du navigateur La raison pour laquelle nous pouvons l'appeler directement est tout comme les constructeurs de String et Array.

La première étape de new Promise(executor) est comme les autres constructeurs. Il construit un nouvel objet selon le prototype de Promise et initialise plusieurs slots internes [[PromiseState]], [[PromiseResult] ], [[PromiseFullfillReactions». ]], [[PromiseRejectReactions]], [[PromiseIsHandled]] pour enregistrer certaines informations pertinentes. Leurs fonctions peuvent être grossièrement déduites des noms. Nous mentionnerons les détails ci-dessous. Ici, leurs valeurs initiales sauf [[PromiseResult]] sont "en attente", liste vide, liste vide et false.

Étape suivante, ES générera la fonction de résolution utilisée pour résoudre la promesse et la fonction de rejet utilisée pour rejeter la promesse en fonction de cet objet de promesse. Appelez ensuite l'exécuteur avec la fonction de résolution et la fonction de rejet comme paramètres. Si une erreur se produit au cours de ce processus, rejetez directement la promesse. Enfin retour promis.

Alors qu'est-ce qu'une résolution et qu'est-ce qu'un rejet ? Nous savons que l'état de la promesse, c'est-à-dire [[PromiseState]], a trois valeurs : en attente, rempli, rejeté. Utilisez la fonction de rejet pour rejeter la promesse et changer son état d'en attente à rejeté. Cependant, la fonction de résolution peut soit remplir une promesse pour changer le statut de la promesse de en attente à remplie, soit être utilisée pour rejeter une promesse.

Alors, que font les fonctions de résolution et de rejet ?

Regardons d'abord la fonction de rejet. Tout d'abord, lors de sa génération, elle initialisera les slots [[Promise]] et [[AlreadyResolved]], c'est-à-dire l'associera à une promesse. Pendant l'exécution, un paramètre Reason sera transmis, et seulement lorsque [[AlreadyResolved]] est faux, c'est-à-dire qu'il n'a pas été résolu et que le statut est en attente, le retour RejectPromise sera appelé et les paramètres de promesse et de raison seront transmis pour rejeter la promesse, sinon renvoie undéfini.
RejectPromise(promise, Reason), en plus de changer [[PromiseState]] de ending à rejeté, il définira également la valeur du résultat de la promesse [[PromiseResult]] sur raison et supprimera la promesse [[PromiseRejectReactions ]] (Je pense que les lecteurs ont compris qu'il y aura une opération pour stocker les enregistrements dans cet emplacement interne plus tard), et utilisent TriggerPromiseReactions pour appeler ces enregistrements pour un traitement ultérieur et transmettre la raison du rejet. De même, l'opération FullfillPromise(promise, value) utilisée dans la fonction de résolution modifie le statut de la promesse en rempli, extrait la valeur de [[PromiseFullfillReactions]], appelle TriggerPromiseReactions et transmet la valeur du résultat rempli.

TriggerPromiseReactions(reactions, argument) appellera EnqueueJob("PromiseJobs", PromiseReactionJob, ff21c191a09e894464c8d010128973f7>), qui sera expliqué en détail plus tard.

Regardons la fonction de résolution Comme la fonction de rejet, lorsqu'elle sera générée, elle sera associée à une promesse. Lors de l'exécution, le paramètre que nous transmettons est appelé résolution. Si la promesse est résolue, undefined est renvoyé. La situation après cela est relativement compliquée.

1. Si l'utilisateur transmet la promesse elle-même à la fonction de résolution comme paramètre de résolution, une TypeError sera créée, levée et RejectPromise sera appelée. Le paramètre de raison est ce TypeError.
2. Si le type de résolution n'est pas Object, appelez FulfillPromise(promise, résolution).
3. Les cas restants sont ceux où la résolution est un objet (Promesse) avec alors autre que lui-même.
Si la résolution est un objet sans alors, RejectPromise.
S'il existe un attribut then mais qu'il ne peut pas être appelé, FulfillPromise, .
S'il existe un attribut then et qu'il peut être appelé, il suffit de EnqueueJob("PromiseJobs", PromiseResolveThenableJob, 8e2521930d3ebfbbc722f93933512ec0>).
Avant d'expliquer EnqueueJob, jetons d'abord un coup d'œil à ce qu'est Job. En termes simples, cela ressemble au mécanisme d'implémentation interne d'un rappel : "Lorsqu'aucun autre ES n'est en cours d'exécution, initialisez et exécutez son propre ES correspondant." Nous avons une file d'attente de tâches FIFO à exécuter, ainsi que l'environnement d'exécution actuel exécutant le contexte d'exécution et la pile de contexte d'exécution. Lorsque les deux dernières sont vides, la première file d'attente de tâches sera exécutée.

ES stipule qu'il doit y avoir au moins deux files d'attente de tâches dans l'implémentation, ScriptJobs et PromiseJobs. Lorsque nous appelons EnqueueJob("PromiseJobs", ...), les Jobs à terminer et leurs paramètres sont insérés dans la file d'attente PromiseJobs. Comme vous pouvez le voir, il existe deux types de Job

1 sous PromiseReactionJob(reaction, argument)
reaction a trois emplacements internes [[Capability]], [[Type]] et [[Handler». ]] , représentant respectivement [[promesse associée et fonction de résolution et fonction de rejet associées]], [[catégorie]], [[gestionnaire]]. Si l'utilisateur ne fournit pas de gestionnaire (non défini), l'argument est utilisé comme résultat selon que la catégorie est Fulfill ou Reject. Si un gestionnaire est donné, il est utilisé pour le traitement ultérieur de l'argument. Enfin, utilisez la fonction de résolution et la fonction de rejet pour traiter et renvoyer en fonction de ce résultat.
2. PromiseResolveThenableJob(promiseToResolve, thenable, then)
Créez la fonction de résolution et la fonction de rejet associées à promiseToResolve. Utilisez alors comme fonction appelante, puis utilisable comme ceci, résolvez la fonction et rejetez la fonction comme paramètres à appeler et à renvoyer.

Promise.prototype.then(onfulfilled, onrejected)

La première consiste à créer une promesseCapability, qui contient une nouvelle promesse et la fonction de résolution et la fonction de rejet associées. La génération de promesse consiste à créer une promesse comme si vous utilisiez normalement le constructeur Promise, mais l'exécuteur transmis au constructeur est automatiquement créé en interne et sa fonction est d'enregistrer la fonction de résolution/rejet dans PromiseCapability. Créez deux PromiseReactions pour remplir et rejeter respectivement en fonction de promiseCapability et onfulfilled/onrejected, qui sont les opérations finales à effectuer dans PromiseJobs. Si la promesse actuelle (this) est à l'état d'attente, insérez ces deux réactions respectivement dans les files d'attente [[PromiseFulfillReactions]] et [[PromiseRejectReactions]] de la promesse. Mais si la promesse est déjà remplie ou rejetée à ce moment-là, la valeur résultat est extraite du [[PromiseResult]] de la promesse et insérée dans la file d'attente des tâches en tant que résultat/raison de rejet rempli("PromiseJobs", PromiseReactionJob. , a26f47e9cf9d76f551506ca748f96cb6>), et renvoie enfin la nouvelle promesse stockée dans prjomiseCapability. Promise.prototype.catch(onrejected) est Promise.prototype.then(undefined, onrejected)

Promise.resolve(x)

Créez une promiseCapability comme then, Then appelez directement la fonction de résolution et transmettez la valeur x à résoudre, et enfin renvoyez la nouvelle promesse.

Promise.all(iterable)

Promise.all A promiseCapability sera également créé comme alors, qui contient une nouvelle promesse et sa fonction de résolution et sa fonction de rejet associées, puis combinée avec la boucle de l'itérateur : 1. Si l'itération est terminée et que le compteur est à 0, appelez la fonction de résolution de promiseCapability pour solve Tableau de résultats 2. Sinon, le compteur est incrémenté de 1, puis la valeur de l'itération suivante est extraite, transmise à Promise.resolve et une nouvelle promesse est également construite, puis une fonction d'élément Promise.all Resolve est créée en interne. , puis transmis à cette nouvelle promesse est utilisé pour résoudre Ajoutez le résultat au tableau de résultats et décrémentez le compteur de un.

Promise.race(iterable)
De même, créez une promesseCapability, puis itérez, utilisez Promise.resolve pour créer une nouvelle promesse, puis appelez la méthode then de cette nouvelle promesse et transmettez-la à promiseCapability. La fonction de résolution/rejet, combinée à la promesse mentionnée précédemment ne se résoudra qu'une seule fois, montre qu'elle ressemble en effet beaucoup à une course.

Conclusion : Après avoir vu cela, je me demande si tout le monde a une compréhension plus profonde de Promise. En allant plus loin, le nouveau async/await proposé dans ES6 applique en fait les idées de Generator et Promise. Si vous êtes intéressé, vous pouvez continuer à en apprendre davantage.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn