Heim  >  Artikel  >  Web-Frontend  >  Vertieftes Verständnis von Versprechen in JS

Vertieftes Verständnis von Versprechen in JS

yulia
yuliaOriginal
2018-09-10 14:52:321398Durchsuche

Bei der Verwendung von Promise besteht unser einfachstes Verständnis und unsere einfachste Verwendung darin, das asynchrone Ergebnis zur Auflösung als Parameter wie den obigen Code bereitzustellen und dann eine benutzerdefinierte Funktion als Ergebnisverarbeitungsfunktion an die Methode then zu übergeben. Aber was genau sind die beiden Parameter „resolve“ und „reject“? Was ist hinter den Kulissen die grundlegende Arbeitsweise? Werfen wir einen ersten Blick darauf aus einer normativen Perspektive.

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

TL;DR
1 Der Arbeitsmechanismus von Promise ähnelt dem von Callback. Beide verwenden die interne abstrakte Operation Job, um asynchron zu implementieren.
2 Promise-Konstruktor Die /reject-Funktion wird intern erstellt, und der beim Aufruf übergebene Parameter ist das zu analysierende Ergebnis, das zusammen mit der vom Benutzer übergebenen Verarbeitungsfunktion, die im Versprechen gespeichert wurde, in die Jobwarteschlange eingefügt wird. Der übergebene Parameter kann auch ein Versprechen sein, das intern in Promise.all/race verwendet wird.
3. Promise.prototype.then bestimmt entsprechend dem aktuellen Promise-Status, ob das im Promise gespeicherte Ergebnis sofort herausgenommen und zusammen mit der Verarbeitungsfunktion im Parameter direkt in die Jobwarteschlange eingefügt oder mit diesem verknüpft werden soll das Versprechen zuerst und verarbeiten Sie es als Ergebnisfunktion. Ruft dann implizit die Konstruktionsfunktion „Promise“ auf, um ein neues Versprechen zu erstellen und es zurückzugeben.
4. Promise.all erstellt zunächst ein neues Versprechen, initialisiert dann ein leeres Ergebnisarray und einen Zähler, um die gelösten Versprechen zu zählen, und iteriert dann. Für jeden Iterationswert wird ein Versprechen erstellt und die Versprechen dann auf gesetzt Fügen Sie das Ergebnis und den Zähler zum Ergebnisarray hinzu. Das Endergebnis wird aufgelöst, wenn der Zähler auf 0 sinkt.
5. Promise.race erstellt außerdem ein neues Hauptversprechen. Basierend auf der Einschränkung, dass das Versprechen nur einmal gelöst werden kann, wird für jeden Iterationswert ein weiteres Versprechen erstellt Durch das Hauptversprechen wird zuerst das Ergebnis zurückgegeben.

neues Promise(Executor)

Beginnen wir zunächst mit dem Promise-Konstruktor. Es handelt sich um den Wert des Promise-Attributs des globalen Objekts in der Browserumgebung Der Grund, warum wir es direkt aufrufen können, ist genau wie bei den Konstruktoren von String und Array.

Der erste Schritt von new Promise(executor) ist genau wie bei anderen Konstruktoren. Er erstellt ein neues Objekt gemäß Promises Prototyp und initialisiert mehrere interne Slots [[PromiseState]], [[PromiseResult]], [[PromiseFullfillReactions ]], [[PromiseRejectReactions]], [[PromiseIsHandled]], um einige relevante Informationen aufzuzeichnen. Ihre Funktionen können aus den folgenden Namen grob abgeleitet werden. Hier sind ihre Anfangswerte außer [[PromiseResult]] „ausstehend“, leere Liste, leere Liste und falsch.

Im nächsten Schritt generiert ES die Auflösungsfunktion zum Auflösen des Versprechens und die Ablehnungsfunktion zum Ablehnen des Versprechens basierend auf diesem Versprechensobjekt. Rufen Sie dann den Executor mit der Auflösungsfunktion und der Ablehnungsfunktion als Parameter auf. Wenn während dieses Vorgangs ein Fehler auftritt, lehnen Sie das Versprechen direkt ab. Endlich Rückgabeversprechen.

Was ist dann Entschlossenheit und was Ablehnung? Wir wissen, dass der Status von Promise, also [[PromiseState]], drei Werte hat: ausstehend, erfüllt, abgelehnt. Verwenden Sie die Ablehnungsfunktion, um das Versprechen abzulehnen und seinen Status von ausstehend in abgelehnt zu ändern. Die Auflösungsfunktion kann jedoch entweder ein Versprechen erfüllen, um den Status des Versprechens von „Ausstehend“ in „erfüllt“ zu ändern, oder sie kann verwendet werden, um ein Versprechen abzulehnen.

Was bewirken also die Auflösungsfunktion und die Ablehnungsfunktion?

Schauen wir uns zunächst die Ablehnungsfunktion an. Beim Generieren werden zunächst die Slots [[Promise]] und [[AlreadyResolved]] initialisiert, das heißt, sie wird mit einem Versprechen verknüpft. Während der Ausführung wird ein Parametergrund übergeben, und nur wenn [[AlreadyResolved]] falsch ist, d übergeben, um das Versprechen abzulehnen. Andernfalls wird undefiniert zurückgegeben.
RejectPromise(promise, reason): Zusätzlich zur Änderung von [[PromiseState]] von „Ausstehend“ in „Abgelehnt“ wird auch der Wert des Versprechensergebnisses [[PromiseResult]] auf „Grund“ gesetzt und die [[PromiseRejectReactions] des Versprechens herausgenommen ]] (Ich glaube, die Leser haben verstanden, dass es später einen Vorgang zum Speichern von Datensätzen in diesem internen Slot geben wird) und verwenden Sie TriggerPromiseReactions, um diese Datensätze zur späteren Verarbeitung aufzurufen und den Grund für die Ablehnung zu übergeben. In ähnlicher Weise ändert die in der Auflösungsfunktion verwendete Operation „FullfillPromise(promise, value)“ den Status des Versprechens in „erfüllt“, extrahiert den Wert von [[PromiseFullfillReactions]], ruft TriggerPromiseReactions auf und übergibt den erfüllten Ergebniswert.

TriggerPromiseReactions(reactions, argument) ruft EnqueueJob("PromiseJobs", PromiseReactionJob, ff21c191a09e894464c8d010128973f7>) auf, was später ausführlich erläutert wird.

Sehen wir uns die Auflösungsfunktion an. Wenn sie generiert wird, wird sie mit einem Versprechen verknüpft. Bei der Ausführung wird der Parameter, den wir übergeben, als Auflösung bezeichnet. Wenn das Versprechen aufgelöst wurde, wird undefiniert zurückgegeben. Die Situation danach ist relativ kompliziert.

1. Wenn der Benutzer das Versprechen selbst als Auflösungsparameter übergibt, wird ein TypeError erstellt, ausgelöst und RejectPromise aufgerufen. Der Grundparameter ist dieser TypeError.
2. Wenn der Auflösungstyp nicht Objekt ist, rufen Sie FulfillPromise(promise,solution) auf.
3. In den übrigen Fällen handelt es sich bei der Lösung um ein anderes Objekt (Versprechen) als sich selbst.
Wenn die Auflösung ein Objekt ohne then ist, RejectPromise.
Wenn ein then-Attribut vorhanden ist, aber nicht aufgerufen werden kann, auch FulfillPromise, .
Wenn es ein then-Attribut gibt und es aufgerufen werden kann, einfach EnqueueJob("PromiseJobs", PromiseResolveThenableJob, 1bc447cf0b2bbc55418402ce5de678a7>).
Bevor wir EnqueueJob erklären, werfen wir zunächst einen Blick darauf, was Job ist. Einfach ausgedrückt ähnelt es dem internen Implementierungsmechanismus eines Rückrufs: „Wenn kein anderes ES ausgeführt wird, initialisieren Sie das entsprechende ES und führen Sie es aus.“ Wir haben eine auszuführende FIFO-Jobwarteschlange sowie einen Ausführungskontext und einen Ausführungskontextstapel in der aktuellen Ausführungsumgebung. Wenn die beiden letztgenannten leer sind, wird die erste Jobwarteschlange ausgeführt.

ES legt fest, dass in der Implementierung mindestens zwei Jobwarteschlangen vorhanden sein müssen, ScriptJobs und PromiseJobs. Wenn wir EnqueueJob("PromiseJobs", ...) aufrufen, werden der abzuschließende Job und seine Parameter in die PromiseJobs-Warteschlange eingefügt. Wie Sie sehen können, gibt es zwei Arten von Jobs

1 unter PromiseReactionJob(reaction, argument)
reaction hat drei interne Slots [[Capability]], [[Type]] und [[Handler ]] , die jeweils [[zugehöriges Versprechen und zugehörige Auflösungsfunktion und Ablehnungsfunktion]], [[Kategorie]], [[Handler]] darstellen. Wenn der Benutzer keinen Handler (undefiniert) angibt, wird das Argument als Ergebnis verwendet, abhängig davon, ob die Kategorie „Erfüllen“ oder „Ablehnen“ ist. Wenn ein Handler angegeben ist, wird dieser für die weitere Verarbeitung des Arguments verwendet. Verwenden Sie abschließend die Auflösungsfunktion und die Ablehnungsfunktion, um das Ergebnis basierend auf diesem Ergebnis zu verarbeiten und zurückzugeben.
2. PromiseResolveThenableJob(promiseToResolve, thenable, then)
Erstellen Sie die Auflösungsfunktion und die Ablehnungsfunktion, die mit PromiseToResolve verknüpft sind. Verwenden Sie „then“ als aufrufende Funktion, „thenable“ als „this“, „resolve function“ und „reject function“ als Parameter zum Aufrufen und Zurückgeben.

Promise.prototype.then(onfulfilled, onrejected)

Die erste besteht darin, eine PromiseCapability zu erstellen, die ein neues Versprechen und die zugehörige Auflösungsfunktion und Ablehnungsfunktion enthält. Die Generierung von Versprechen besteht darin, ein Versprechen zu erstellen, genau wie bei der normalen Verwendung des Promise-Konstruktors, aber der an den Konstruktor übergebene Executor wird automatisch intern erstellt und seine Funktion besteht darin, die Auflösungs-/Ablehnungsfunktion in PromiseCapability aufzuzeichnen. Erstellen Sie zwei PromiseReactions für „fill“ und „reject“, basierend auf PromiseCapability und onfulfilled/onrejected, den letzten Vorgängen, die in PromiseJobs ausgeführt werden sollen. Wenn sich das aktuelle Versprechen (this) im Status „Ausstehend“ befindet, fügen Sie diese beiden Reaktionen in die Warteschlangen [[PromiseFulfillReactions]] und [[PromiseRejectReactions]] des Versprechens ein. Wenn das Versprechen jedoch zu diesem Zeitpunkt bereits erfüllt oder abgelehnt wurde, wird der Wert result aus dem [[PromiseResult]] des Versprechens entnommen und als erfülltes Ergebnis/Ablehnungsgrund in die Jobwarteschlange eingefügt , a26f47e9cf9d76f551506ca748f96cb6>) und gibt schließlich das neue Versprechen zurück, das in prjomiseCapability gespeichert ist. Promise.prototype.catch(onrejected) ist Promise.prototype.then(undefiniert, onrejected)

Promise.resolve(x)

Erstellen Sie eine PromiseCapability wie then, Then Rufen Sie direkt die Auflösungsfunktion auf, übergeben Sie den aufzulösenden Wert x und geben Sie schließlich das neue Versprechen zurück.

Promise.all(iterable)

Promise.all A So wird auch PromiseCapability erstellt, das ein neues Promise und die zugehörige Auflösungsfunktion und Ablehnungsfunktion enthält, und dann mit der Iteratorschleife kombiniert: 1. Wenn die Iteration abgeschlossen ist und der Zähler 0 ist, rufen Sie die Auflösungsfunktion von PromiseCapability auf Auflösung Ergebnis-Array 2. Andernfalls wird der Zähler um 1 erhöht, dann wird der Wert der nächsten Iteration herausgenommen, an Promise.resolve übergeben und es wird auch ein neues Versprechen erstellt, und dann wird intern eine Promise.all-Resolve-Element-Funktion erstellt , und das dann an dieses neue Versprechen übergebene wird zum Auflösen verwendet. Fügen Sie das Ergebnis zum Ergebnisarray hinzu und dekrementieren Sie den Zähler um eins.

Promise.race(iterable)
In ähnlicher Weise erstellen Sie eine PromiseCapability, iterieren Sie dann, verwenden Sie Promise.resolve, um ein neues Versprechen zu erstellen, rufen Sie dann die Methode then dieses neuen Versprechens auf und übergeben Sie sie an PromiseCapability Die Auflösungs-/Abweisungsfunktion kombiniert mit dem zuvor erwähnten Versprechen, dass sie nur einmal aufgelöst wird, zeigt, dass sie tatsächlich sehr rassenähnlich ist.

Fazit: Nachdem ich das gesehen habe, frage ich mich, ob jeder ein tieferes Verständnis von Promise hat. Um noch einen Schritt weiter zu gehen, wendet das neu vorgeschlagene Async/Await in ES6 tatsächlich die Ideen von Generator und Promise an. Wenn Sie interessiert sind, können Sie weiter erfahren.

Das obige ist der detaillierte Inhalt vonVertieftes Verständnis von Versprechen in JS. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn