Was ist der Unterschied zwischen Rückgabewert oder Promise.resolve von da an ()

314

Was ist der Unterschied zwischen:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return "bbb";
  })
  .then(function(result) {
    console.log(result);
  });

und das:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return Promise.resolve("bbb");
  })
  .then(function(result) {
    console.log(result);
  });

Ich frage, wie ich ein anderes Verhalten bekomme. Verwenden des Angular- und $ http-Dienstes mit Verkettung .then (). Ein bisschen zu viel Code, daher zuerst das obige Beispiel.

Spirytus
quelle
1
Welches "andere Verhalten" sehen Sie? Beide Beispiele sollten funktionieren und sich ungefähr gleich verhalten. Das Promise.resolve()im zweiten Beispiel ist unnötig.
JLRishe
4
@pixelbits Es ist überhaupt nichts Falsches thendaran, ein Versprechen von einem Handler zurückzugeben. Tatsächlich ist es ein Schlüsselaspekt der Versprechensspezifikation, dass Sie dies tun können.
Beachten Sie, dass dies mit willkürlich verschachtelten thens funktioniert - der Begriff "andere Sprachen" thenlautet sowohl a mapals auch a flatMap.
Benjamin Gruenbaum
1
Warum müssen Sie in Zeile 2 res ("aaa") aufrufen, warum kann "aaa" nicht ausreichend zurückgegeben werden und der Promise-Catch für Auflösung () auf dieselbe Weise wie Ausnahmen für "Reject" () abgefangen werden?
Sam Liddicott
1
@SamLiddicott hat die gleiche Frage, während Minen etwas komplizierter sind: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result));Dieser Code bleibt einfach hängen (nicht für immer gelöst). Aber wenn ich return "haha";zu return res("haha");dann wechsle , wird es funktionieren und "haha" alarmieren. Hat der fetch (). Then () nicht schon "haha" in ein gelöstes Versprechen eingewickelt?
Shaung Cheng

Antworten:

138

Die Regel lautet: Wenn die im thenHandler enthaltene Funktion einen Wert zurückgibt, wird das Versprechen mit diesem Wert aufgelöst / abgelehnt. Wenn die Funktion ein Versprechen zurückgibt, ist die nächste thenKlausel die thenKlausel des Versprechens, das die Funktion zurückgegeben hat In diesem Fall durchläuft das erste Beispiel die normale Reihenfolge der thensund druckt Werte aus, wie zu erwarten ist. Im zweiten Beispiel wird das Versprechen-Objekt, das zurückgegeben wird, wenn Sie dies tun Promise.resolve("bbb"), thenbeim Verketten aufgerufen (im Grunde). Die Art und Weise, wie es tatsächlich funktioniert, wird unten ausführlicher beschrieben.

Zitat aus der Promises / A + Spezifikation:

Das Versprechenlösungsverfahren ist eine abstrakte Operation, bei der ein Versprechen und ein Wert als Eingabe verwendet werden, die wir als bezeichnen [[Resolve]](promise, x). Wenn xes dann möglich ist, versucht es, das Versprechen in den Zustand von zux versetzen, unter der Annahme, dass sich x zumindest etwas wie ein Versprechen verhält . Ansonsten erfüllt es das Versprechen mit dem Wert x.

Diese Behandlung von thenables ermöglicht die Interoperabilität von Versprechen-Implementierungen, sofern sie eine Promises / A + -konforme then-Methode offenlegen. Es ermöglicht Promises / A + -Implementierungen auch, nicht konforme Implementierungen mit angemessenen Methoden zu „assimilieren“.

Das Wichtigste hier ist diese Zeile:

Wenn xes ein Versprechen ist, nimm seinen Zustand an [3.4]

Link: https://promisesaplus.com/#point-49

Hrishi
quelle
4
"Adopt its state" ist eine präzise und nützliche Methode, um das Verhalten auszudrücken, wenn ein thenHandler ein Versprechen zurückgibt. +1 für die Spezifikationsreferenz.
69
Tatsächlich - der relevante Teil der Spezifikation hier ist die Tatsache, dass [[Resolve]]sowohl auf thenFähigkeiten als auch auf Werten aufgerufen wird, so dass im Wesentlichen ein Wert mit dem Versprechen umhüllt wird, so dass er return "aaa"derselbe ist return Promise.resolve("aaa")und return Promise.resolve("aaa")derselbe ist wie return Promise.resolve(Promise.resolve("aaa"))- da die Auflösung idempotent ist, ihn auf einen Wert mehr aufzurufen als einmal hat das gleiche Ergebnis.
Benjamin Gruenbaum
8
@Benjamin Gruenbaum bedeutet das, dass Rückkehr "aaa"und in jedem Fall return Promise.resolve("aaa")in thenFähigkeiten austauschbar sind?
CSnerd
9
Ja, genau das bedeutet es.
Benjamin Gruenbaum
118

In einfachen Worten, innerhalb eines then Handlerfunktion:

A) Wann xist ein Wert (Zahl, Zeichenfolge usw.):

  1. return x ist äquivalent zu return Promise.resolve(x)
  2. throw x ist äquivalent zu return Promise.reject(x)

B) Wann xist ein Versprechen bereits erfüllt (nicht mehr ausstehend):

  1. return x ist äquivalent zu return Promise.resolve(x) , wenn das Versprechen bereits gelöst wurde.
  2. return xist gleichbedeutend mit return Promise.reject(x), wenn das Versprechen bereits abgelehnt wurde.

C) Wann xsteht ein Versprechen aus?

  1. return xgibt ein ausstehendes Versprechen zurück und es wird im folgenden ausgewertet then.

Weitere Informationen zu diesem Thema finden Sie in den Dokumenten Promise.prototype.then () .

Arian Acosta
quelle
93

Ihre beiden Beispiele sollten sich ziemlich gleich verhalten.

Ein in einem then()Handler zurückgegebener Wert wird zum Auflösungswert des von diesem zurückgegebenen Versprechens then(). Wenn der innerhalb von zurückgegebene Wert .then ein Versprechen ist, wird das von zurückgegebene Versprechen then()den Zustand dieses Versprechens "annehmen" und genau wie das zurückgegebene Versprechen auflösen / ablehnen.

In Ihrem ersten Beispiel kehren Sie "bbb"im ersten then()Handler zurück und werden "bbb"an den nächsten then()Handler übergeben.

In Ihrem zweiten Beispiel geben Sie ein Versprechen zurück, das sofort mit dem Wert aufgelöst "bbb"und "bbb"an den nächsten then()Handler übergeben wird. (Das Promise.resolve()hier ist irrelevant).

Das Ergebnis ist das gleiche.

Wenn Sie uns ein Beispiel zeigen können, das tatsächlich ein anderes Verhalten zeigt, können wir Ihnen sagen, warum dies geschieht.

JLRishe
quelle
1
Gute Antwort! Was ist mit Promise.resolve();vs return;?
FabianTe
2
@FabianTe Diese hätten auch den gleichen Effekt, außer mit undefinedstatt "bbb".
JLRishe
51

Sie haben bereits eine gute formelle Antwort erhalten. Ich dachte, ich sollte eine kurze hinzufügen.

Die folgenden Dinge sind identisch mit Versprechen / A + Versprechen:

  • Anrufen Promise.resolve(In Ihrem Angular-Fall ist das$q.when )
  • Den Versprechenskonstruktor aufrufen und in seinem Resolver auflösen. In deinem Fall ist das so new $q.
  • Rückgabe eines Wertes aus einem thenRückruf.
  • Rufen Sie Promise.all für ein Array mit einem Wert auf und extrahieren Sie diesen Wert.

Für ein Versprechen oder einen einfachen Wert X sind also alle identisch:

Promise.resolve(x);
new Promise(function(resolve, reject){ resolve(x); });
Promise.resolve().then(function(){ return x; });
Promise.all([x]).then(function(arr){ return arr[0]; });

Und es ist keine Überraschung, dass die Versprechensspezifikation auf dem Versprechenslösungsverfahren basiert, das eine einfache Interaktion zwischen Bibliotheken (wie $ q und native Versprechungen) ermöglicht und Ihr Leben insgesamt erleichtert. Wann immer eine Versprechenslösung auftreten könnte, erfolgt eine Lösung, die eine allgemeine Konsistenz schafft.

Benjamin Gruenbaum
quelle
Darf ich fragen, wozu das gut ist Promise.resolve().then(function(){ return x; });? Ich fand einen Ausschnitt, der etwas Ähnliches tat (es rief eine Funktion innerhalb des thenBlocks auf). Ich dachte, es wäre mehr oder weniger wie eine Auszeit, aber es ist ein bisschen schneller. jsben.ch/HIfDo
Sampgun
Es macht keinen Sinn, dass es in 99,99% der Fälle mit Promise.resolve (x) identisch ist. (0,001% sind, dass wir uns in einem withBlock über einem Objekt oder Proxy mit einem xEigenschaftszugriff befinden, der eine Ausnahme auslöst. In diesem Fall würde Promise.resolve (x) einen ausgelösten Fehler verursachen, Promise.resolve().then(function(){ return x; });wäre jedoch ein abgelehntes Versprechen, da der Fehler ausgelöst wird in a then).
Benjamin Gruenbaum
Sie haben einen leeren Blitz verknüpft oder nicht gespeichert. Jedenfalls habe ich nicht über die Unterschiede zwischen den Aussagen gesprochen. Ich habe genau darüber gesprochen, was ich geschrieben habe. Um es klarer zu machen, dies ist der Ausschnitt, über den ich gesprochen habe : if (validator) { Promise.resolve().then(() => { this._cdRef.markForCheck(); }); }. Hier wird das Versprechen nicht vergeben. Worum geht es also? Eine Auszeit hätte (mehr oder weniger) den gleichen Effekt oder nicht?
Sampgun
1
Der Aufruf wird asynchron ausgeführt, nachdem der gesamte synchrone Code ausgeführt wurde, jedoch bevor E / A ausgeführt werden. Das nennt man "Mikrotick-Semantik".
Benjamin Gruenbaum