
“ Master the JavaScript Interview “ ist eine Reihe von Beiträgen, die Kandidaten auf häufige Fragen vorbereiten sollen, denen sie wahrscheinlich begegnen werden, wenn sie sich für eine JavaScript-Position auf mittlerer bis hoher Ebene bewerben. Dies sind Fragen, die ich häufig in echten Interviews verwende.
Ein promise ist ein Objekt, das irgendwann in der Zukunft einen einzelnen Wert erzeugen kann: entweder einen aufgelösten Wert oder einen Grund, dass es nicht aufgelöst wurde (z. B. ist ein Netzwerkfehler aufgetreten)., Ein Versprechen kann sich in einem von drei möglichen Zuständen befinden: erfüllt, abgelehnt oder anhängig. Versprechen Benutzer können Rückrufe anhängen, um den erfüllten Wert oder den Grund für die Ablehnung zu behandeln.
Versprechen sind eifrig, was bedeutet, dass ein Versprechen beginnt, jede Aufgabe zu erledigen, die Sie ihm geben, sobald der Versprechen-Konstruktor aufgerufen wird. Wenn Sie Hilfe benötigen, schauen Sie sich Observablen oder Aufgaben an.
Eine unvollständige Geschichte von Versprechungen
Frühe Implementierungen von Versprechungen und Futures (eine ähnliche / verwandte Idee) tauchten bereits in den 1980er Jahren in Sprachen wie MultiLisp und Concurrent Prolog auf., Die Verwendung des Wortes „Versprechen“ wurde 1988 von Barbara Liskov und Liuba Shrira geprägt.
Als ich das erste Mal von Versprechungen in JavaScript hörte, war Node brandneu und die Community diskutierte über den besten Umgang mit asynchronem Verhalten. Die Community experimentierte eine Weile mit Versprechungen, entschied sich jedoch schließlich für die Node-Standard Error-First-Rückrufe.
Ungefähr zur gleichen Zeit fügte Dojo Versprechungen über die verzögerte API hinzu. Wachsendes Interesse und Aktivität führten schließlich zu den neu gebildeten Promises / A-Spezifikationen, die verschiedene Versprechen interoperabler machen sollten.,
Das asynchrone Verhalten von jQuery wurde um Versprechen herum umgestaltet. jQuery Versprechen Unterstützung hatte bemerkenswerte Ähnlichkeiten mit Dojos Versprechen, und es wurde schnell die am häufigsten verwendete Versprechen Implementierung in JavaScript aufgrund jQuery immense Popularität — für eine Zeit. Es unterstützte jedoch nicht das Verkettungsverhalten mit zwei Kanälen (erfüllt/abgelehnt) & exception management, auf das die Leute zählten, um Tools auf Versprechen aufzubauen.,
Trotz dieser Schwächen machte jQuery offiziell JavaScript-Versprechen zum Mainstream und bessere Stand-Alone-Promise-Bibliotheken wie Q, When und Bluebird wurden sehr beliebt. Die Implementierungsunverträglichkeiten von jQuery haben einige wichtige Klarstellungen in der Promise-Spezifikation ausgelöst, die als Promises/A+ – Spezifikation neu geschrieben und umbenannt wurde.,
ES6 brachte eine Promises/A+ – konforme Promise
global, und einige sehr wichtige APIs wurden auf der neuen Standard-Promise-Unterstützung aufgebaut: insbesondere die WHATWG Fetch-Spezifikation und der Async Functions-Standard (ein Entwurf der Stufe 3 zum Zeitpunkt dieses Schreibens).
Die hier beschriebenen Versprechungen sind mit der Spezifikation Promises/A+ kompatibel, wobei der Fokus auf der Implementierung des ECMAScript-Standards Promise
liegt.
Wie Versprechen funktionieren
Ein Versprechen ist ein Objekt, das synchron von einer asynchronen Funktion zurückgegeben werden kann., Es befindet sich in einem von drei möglichen Zuständen:
Ein Versprechen wird erfüllt, wenn es nicht anhängig ist (es wurde aufgelöst oder abgelehnt). Manchmal benutzen Leute aufgelöst und angesiedelt, um dasselbe zu bedeuten: nicht anhängig.
Einmal abgewickelt, kann ein Versprechen nicht umgesiedelt werden. Der erneute Aufruf von resolve()
oder reject()
hat keine Auswirkungen. Die Unveränderlichkeit eines besicherten Versprechens ist ein wichtiges Merkmal.
Native JavaScript-Versprechungen machen Versprechungszustände nicht verfügbar. Stattdessen wird erwartet, dass Sie das Versprechen als Blackbox behandeln., Nur die Funktion, die für das Erstellen des Versprechens verantwortlich ist, kennt den Versprechungsstatus oder Zugriff zum Auflösen oder Ablehnen.
Hier ist eine Funktion, die ein Versprechen zurückgibt, das nach einer bestimmten Zeitverzögerung aufgelöst wird:
Unser wait(3000)
Aufruf wartet 3000ms (3 Sekunden), und dann log 'Hello!'
., Alle spec-kompatiblen Versprechen definieren eine .then()
– Methode, mit der Sie Handler übergeben, die den aufgelösten oder abgelehnten Wert annehmen können.
Der ES6 promise Konstruktor nimmt eine Funktion an. Diese Funktion verwendet zwei Parameter, resolve()
und reject()
. Im obigen Beispiel verwenden wir nur resolve()
, also habe ich reject()
aus der Parameterliste gelassen. Dann rufen wir setTimeout()
auf, um die Verzögerung zu erstellen, und rufen resolve()
auf, wenn es fertig ist.,
Optional können Sie resolve()
oder reject()
mit Werten versehen, die an die mit .then()
verbundenen Rückruffunktionen übergeben werden.
Wenn ich reject()
mit einem Wert übergebe, übergebe ich immer ein Error
Objekt. Im Allgemeinen möchte ich zwei mögliche Auflösungszustände: den normalen Happy Path oder eine Ausnahme — alles, was den normalen Happy Path daran hindert, zu geschehen. Das Übergeben einesError
– Objekts macht dies explizit.,
Wichtige Promise Rules
Ein Standard für Promises wurde von der Promises / A+ specification Community definiert. Es gibt viele Implementierungen, die dem Standard entsprechen, einschließlich der Versprechen des JavaScript-Standards ECMAScript.
Versprechen nach der Spezifikation muss ein bestimmter Satz von Regeln folgen:
- Ein Versprechen oder „thenable“ist ein Objekt, das eine standardkonforme
.then()
Methode liefert. - Ein ausstehendes Versprechen kann in einen erfüllten oder abgelehnten Zustand übergehen.,
- Ein erfülltes oder abgelehntes Versprechen wird erfüllt und darf nicht in einen anderen Zustand übergehen.
- Sobald ein Versprechen erfüllt ist, muss es einen Wert haben (der
undefined
). Dieser Wert darf sich nicht ändern.
Änderung bezieht sich in diesem Zusammenhang auf identity (===
) Vergleich. Ein Objekt kann als erfüllter Wert verwendet werden, und Objekteigenschaften können mutieren.,
Jedes Versprechen muss eine .then()
– Methode mit folgender Signatur angeben:
promise.then(
onFulfilled?: Function,
onRejected?: Function
) => Promise
Die .then()
– Methode muss diese Regeln erfüllen:
- Sowohl
onFulfilled()
als auchonRejected()
sind optional. - Wenn die angegebenen Argumente keine Funktionen sind, müssen sie ignoriert werden.
-
onFulfilled()
wird aufgerufen, nachdem das Versprechen erfüllt wurde, wobei der Wert des Versprechens das erste Argument ist., -
onRejected()
wird aufgerufen, nachdem das Versprechen abgelehnt wurde, mit dem Grund für die Ablehnung als erstes Argument. Der Grund kann ein gültiger JavaScript-Wert sein, aber da Ablehnungen im Wesentlichen gleichbedeutend mit Ausnahmen sind, empfehle ich die Verwendung von Fehlerobjekten. - Weder
onFulfilled()
nochonRejected()
dürfen mehr als einmal aufgerufen werden. -
.then()
kann viele Male auf dem gleichen Versprechen aufgerufen werden. Mit anderen Worten, ein Versprechen kann verwendet werden, um Rückrufe zu aggregieren., -
.then()
muss ein neues Versprechen zurückgeben,promise2
. - Wenn
onFulfilled()
oderonRejected()
Rückgabe eines Wertesx
undx
ist ein Versprechen,promise2
wird die Sperre in der mit (angenommen, die gleichen Zustand und den Wert als)x
. Andernfalls wirdpromise2
mit dem Wertx
erfüllt., - Wenn entweder
onFulfilled
oderonRejected
wirft eine exceptione
,promise2
müssen abgelehnt werden mite
als Grund. - Wenn
onFulfilled
keine Funktion ist undpromise1
erfüllt ist, musspromise2
mit dem gleichen Wert wiepromise1
erfüllt sein., - Wenn
onRejected
keine Funktion ist undpromise1
abgelehnt wird, musspromise2
aus demselben Grund wiepromise1
abgelehnt werden.
Promise Chaining
Da .then()
immer ein neues Versprechen zurückgibt, ist es möglich, Versprechen mit genauer Kontrolle darüber zu verketten, wie und wo Fehler behandelt werden. Versprechungen ermöglichen es Ihnen, das Verhalten von try
/catch
normalen synchronen Code nachzuahmen.,
Wie synchroner Code führt die Verkettung zu einer Sequenz, die seriell ausgeführt wird., Mit anderen Worten, Sie können Folgendes tun:
fetch(url)
.then(process)
.then(save)
.catch(handleErrors)
;
Hier ist ein Beispiel für eine komplexe Versprechungskette mit mehreren Ablehnungen:
Error Handling
Beachten Sie, dass Versprechungen sowohl einen Erfolg als auch einen Fehlerbehandler haben, und es ist sehr üblich, Code zu sehen, der dies tut:
save().then(
handleSuccess,
handleError
);
Aber was passiert, wenn handleSuccess()
einen Fehler auslöst?, Das von .then()
zurückgegebene Versprechen wird abgelehnt, aber es gibt nichts, was die Ablehnung abfangen könnte — was bedeutet, dass ein Fehler in Ihrer App verschluckt wird. Hoppla!
Aus diesem Grund betrachten einige Leute den obigen Code als Anti-Muster und empfehlen stattdessen Folgendes:
save()
.then(handleSuccess)
.catch(handleError)
;
Der Unterschied ist subtil, aber wichtig. Im ersten Beispiel wird ein Fehler mit Ursprung in der save()
– Operation abgefangen, aber ein Fehler mit Ursprung in der handleSuccess()
– Funktion wird verschluckt.,

In the second example, .catch()
will handle rejections from either save()
, or handleSuccess()
.

Natürlich kann der save()
– Fehler ein Netzwerkfehler sein, während der handleSuccess()
– Fehler möglicherweise darauf zurückzuführen ist, dass der Entwickler vergessen hat, einen bestimmten Statuscode zu behandeln. Was ist, wenn Sie anders damit umgehen wollen? Sie können sich für beide entscheiden:
save()
.then(
handleSuccess,
handleNetworkError
)
.catch(handleProgrammerError)
;
Was auch immer Sie bevorzugen, ich empfehle, alle Versprechungsketten mit einer .catch()
. Das lohnt sich zu wiederholen:
Ich empfehle, alle Versprechungsketten mit einer
.catch()
zu beenden.,
Wie storniere ich ein Versprechen?
Eines der ersten Dinge, über die sich neue Versprechungsbenutzer oft wundern, ist, wie sie ein Versprechen stornieren können. Hier ist eine Idee: Lehnen Sie das Versprechen einfach mit „Storniert“ als Grund ab. Wenn Sie anders damit umgehen müssen als mit einem „normalen“ Fehler, führen Sie Ihre Verzweigung in Ihrem Fehlerhandler durch.
Hier sind einige häufige Fehler, die Menschen machen, wenn sie ihre eigenen Versprechen Stornierung rollen:
Hinzufügen .,cancel () zum Versprechen
Das Hinzufügen von .cancel()
macht das Versprechen nicht zum Standard, verstößt jedoch auch gegen eine andere Regel von Versprechungen: Nur die Funktion, die das Versprechen erstellt, sollte in der Lage sein, das Versprechen aufzulösen, abzulehnen oder abzubrechen. Das Aussetzen bricht diese Kapselung und ermutigt die Leute, Code zu schreiben, der das Versprechen an Orten manipuliert, die nichts darüber wissen sollten. Vermeiden Sie Spaghetti und gebrochene Versprechen.
Vergessen zu bereinigen
Einige kluge Leute haben herausgefunden, dass es eine Möglichkeit gibt, Promise.race()
als Löschmechanismus zu verwenden., Das Problem dabei ist, dass die Löschungssteuerung von der Funktion übernommen wird, die das Versprechen erstellt, und dies ist der einzige Ort, an dem Sie ordnungsgemäße Bereinigungsaktivitäten durchführen können, z. B. das Löschen von Timeouts oder das Freigeben von Speicher durch Löschen von Verweisen auf Daten usw…
Vergessen, ein abgelehntes Abbrechen-Versprechen zu verarbeiten
Wussten Sie, dass Chrome Warnmeldungen über die gesamte Konsole auslöst, wenn Sie vergessen, eine Versprechen-Ablehnung zu behandeln? Hoppla!
Zu komplex
Der zurückgenommene TC39-Stornierungsvorschlag schlug einen separaten Nachrichtenkanal für Stornierungen vor., Es wurde auch ein neues Konzept namens Cancellation Token verwendet. Meiner Meinung nach hätte die Lösung die Promise-Spezifikation erheblich aufgebläht, und das einzige Merkmal, das dafür gesorgt hätte, dass Spekulationen die Trennung von Ablehnungen und Stornierungen nicht direkt unterstützen, ist die Trennung von Ablehnungen und Stornierungen, die IMO zunächst nicht notwendig ist.
Möchten Sie wechseln, je nachdem, ob eine Ausnahme oder eine Stornierung vorliegt? Ja, absolut. Ist das der Job des Versprechens? Meiner Meinung nach, nein, ist es nicht.,
Promise Cancellation überdenken
Im Allgemeinen übergebe ich alle Informationen, die das Versprechen benötigt, um zu bestimmen, wie es zum Zeitpunkt der Promise-Erstellung aufgelöst / abgelehnt / abgebrochen werden soll. Auf diese Weise ist keine .cancel()
– Methode für ein Versprechen erforderlich. Sie fragen sich vielleicht, wie Sie möglicherweise wissen könnten, ob Sie zur Erstellungszeit abbrechen werden oder nicht.
“ Wenn ich noch nicht weiß, ob ich abbrechen soll oder nicht, woher weiß ich, was ich beim Erstellen des Versprechens weitergeben soll?,“
Wenn es nur eine Art Objekt gäbe, das in Zukunft für einen potenziellen Wert stehen könnte… oh, warte.
Der Wert, den wir übergeben, um darzustellen, ob abgebrochen werden soll oder nicht, könnte selbst ein Versprechen sein. So könnte das aussehen:
Wir verwenden die Standardparameterzuweisung, um anzuweisen, dass sie nicht standardmäßig abgebrochen werden soll. Dadurch ist der Parameter cancel
praktisch optional., Dann setzen wir das Timeout wie zuvor, aber dieses Mal erfassen wir die ID des Timeouts, damit wir es später löschen können.
Wir verwenden diecancel.then()
– Methode, um die Stornierung und Ressourcenbereinigung zu behandeln. Dies wird nur ausgeführt, wenn das Versprechen abgebrochen wird, bevor es aufgelöst werden kann. Wenn Sie zu spät stornieren, haben Sie Ihre Chance verpasst. Der Zug hat den Bahnhof verlassen.
Hinweis: Sie fragen sich vielleicht, wofür die
noop()
– Funktion gedacht ist. Das Wort noop steht für no-op und bedeutet eine Funktion, die nichts tut., Ohne sie löst V8 Warnungen aus:UnhandledPromiseRejectionWarning: Unhandled promise rejection
. Es ist eine gute Idee, Versprechen immer abzulehnen, auch wenn Ihr Handler einenoop()
.
Abstraktion Versprechen Widerrufs
Dies ist in Ordnung für ein wait()
timer, aber wir können Abstrakt diese Idee weiter zu Kapseln, alles was Sie haben zu erinnern:
- Abweisen Abbrechen Versprechen durch Standard, wir don nicht wollen zu stornieren oder zu werfen Fehler, wenn kein Abbrechen Versprechen bekommt übergeben.
- Denken Sie daran, die Bereinigung durchzuführen, wenn Sie Stornierungen ablehnen.,
- Denken Sie daran, dass die
onCancel
Bereinigung selbst einen Fehler auslösen kann und dieser Fehler auch behandelt werden muss. (Beachten Sie, dass die Fehlerbehandlung im obigen Wait-Beispiel weggelassen wird — es ist leicht zu vergessen!)
Lassen Sie uns ein cancellable promise Utility erstellen, mit dem Sie jedes Versprechen umbrechen können., Um beispielsweise Netzwerkanforderungen usw. zu verarbeiten, sieht die Signatur folgendermaßen aus:
speculation(fn: SpecFunction, shouldCancel: Promise) => Promise
Die SpecFunction ist genau wie die Funktion, die Sie an den Promise
Konstruktor übergeben würden, mit einer Ausnahme — sie benötigt eine onCancel()
Handler:
SpecFunction(resolve: Function, reject: Function, onCancel: Function) => Void
Beachten Sie, dass dieses Beispiel nur eine Illustration ist, um Ihnen den Kern der Funktionsweise zu geben. Es gibt einige andere Randfälle, die Sie berücksichtigen müssen., In dieser Version wird beispielsweise handleCancel
aufgerufen, wenn Sie das Versprechen abbrechen, nachdem es bereits abgeschlossen ist.
Ich habe eine gepflegte Produktionsversion davon implementiert, wobei Edge-Fälle als Open-Source-Bibliothek behandelt werden, Spekulation.
Verwenden wir die verbesserte Bibliotheksabstraktion, um das Dienstprogramm cancellable wait()
von zuvor neu zu schreiben., Installieren Sie zuerst die Spekulation:
npm install --save speculation
Jetzt können Sie sie importieren und verwenden:
Dies vereinfacht die Dinge ein wenig, da Sie sich keine Sorgen um die noop()
machen müssen, in Ihrem onCancel()
, Funktion oder andere Randfälle. Diese Details wurden von speculation()
abstrahiert. Überprüfen Sie es und fühlen Sie sich frei, es in realen Projekten zu verwenden.,
Extras des nativen JS-Versprechens
Das nativePromise
– Objekt enthält einige zusätzliche Dinge, an denen Sie interessiert sein könnten:
Promise.reject()
gibt ein abgelehntes Versprechen zurück.-
Promise.resolve()
gibt ein Versprechen gelöst. -
Promise.race()
nimmt ein Array (oder ein beliebiges iterables) und gibt ein Versprechen zurück, das mit dem Wert des ersten aufgelösten Versprechens im iterable aufgelöst wird, oder lehnt es mit dem Grund des ersten Versprechens ab, das abgelehnt wird., -
Promise.all()
nimmt ein Array (oder ein beliebiges iterables) und gibt ein Versprechen zurück, das aufgelöst wird, wenn alle Versprechen im iterablen Argument aufgelöst sind, oder lehnt mit dem Grund des ersten übergebenen Versprechens ab, das abgelehnt wird.
Versprechungen sind zu einem integralen Bestandteil mehrerer Redewendungen in JavaScript geworden, einschließlich des WHATWG Fetch-Standards, der für die meisten modernen Ajax-Anforderungen verwendet wird, und des asynchronen Funktionsstandards, der verwendet wird, um asynchronen Code synchron aussehen zu lassen.,
Asynchrone Funktionen sind zum Zeitpunkt dieses Schreibens Stufe 3, aber ich gehe davon aus, dass sie bald eine sehr beliebte, sehr häufig verwendete Lösung für die asynchrone Programmierung in JavaScript werden — was bedeutet, dass das Lernen, Versprechen zu schätzen, für JavaScript-Entwickler in naher Zukunft noch wichtiger sein wird.
Wenn Sie beispielsweise Redux verwenden, empfehle ich Ihnen, redux-saga: Eine Bibliothek zum Verwalten von Nebenwirkungen in Redux, die in der gesamten Dokumentation von asynchronen Funktionen abhängt.,
Ich hoffe, dass selbst erfahrene Promise-Benutzer besser verstehen, was Versprechen sind und wie sie funktionieren und wie sie sie nach dem Lesen besser nutzen können.
Entdecken Sie die Serie
- Was ist ein Abschluss?
- Was ist der Unterschied zwischen Klassen-und Prototypenvererbung?
- Was ist eine Reine Funktion?
- Was ist Funktionszusammensetzung?
- Was ist Funktionale Programmierung?
- Was ist ein Versprechen?
- Soft Skills