Ob promise.resolve()
synchron oder asynchron seine Fortsetzungen ausführt, hängt wirklich von der Implementierung ab.
Darüber hinaus ist die "Ereignisschleife" nicht der einzige Mechanismus, der einen anderen "Ausführungskontext" bereitstellt. Es kann andere Mittel geben, zum Beispiel Threads oder Thread-Pools, oder man denke an GCD (Grand Central Dispatch, Versand-Lib), die Versandwarteschlangen bereitstellt.
The Promises/A+ Spec erfordert deutlich, daß die Fortsetzung (die jeweils den onFulfilled
onRejected
handler) sein wird asynchron in Bezug auf die „Ausführungskontext“ ausgeführt, in dem die then
Methode aufgerufen wird.
onFulfilled
oder onRejected
darf nicht bis der Stapel enthält Code nur Plattform Ausführungskontext aufgerufen werden. [3.1].
Unter den Notizen können Sie lesen, was das eigentlich bedeutet:
hier „Plattform Code“ Motor, Umwelt und Implementierungscode Versprechen. In der Praxis stellt diese Anforderung sicher, dass onFulfilled und onRejected asynchron ausgeführt werden, nach der Ereignisschleife, die dann aufgerufen wird, und mit einem neuen Stack.
Hier wird jedes Ereignis in einem anderen "Ausführungskontext" ausgeführt, obwohl es sich um die gleiche Ereignisschleife und den gleichen "Thread" handelt.
Da die Promises/A + Spezifikation für die Javascript-Umgebung geschrieben ist, eine allgemeinere Beschreibung würde einfach verlangen, dass die Fortsetzungasynchron ausgeführt mit Bezug auf den Anrufer Aufruf die then
Methode sein wird.
Dafür gibt es gute Gründe!
Beispiel (Pseudocode):
promise = async_task();
printf("a");
promise.then((int result){
printf("b");
});
printf("c");
Unter der Annahme, der Handler (Fortsetzung) auf dem gleiche Gewinde wie die Call-Site ausgeführt wird, soll die Reihenfolge der Ausführung sein, dass die Konsole dies zeigt:
acb
Insbesondere wenn ein Versprechen bereits gelöst wird, neigen einige Implementierungen die Fortsetzung „sofort“ aufzurufen (das heißt synchron) auf dem gleichen Ausführungskontext. Dies würde eindeutig gegen die oben genannte Regel verstoßen.
Der Grund für die Regel die Fortsetzung aufzurufen immer asynchron ist, dass ein Anruf Ort eine Garantie über die relative Reihenfolge der Ausführung der Handler und Code die then
und über die Fortführung Anweisung nach in jedem Szenario haben muss. Das heißt, unabhängig davon, ob ein Versprechen bereits gelöst ist oder nicht, muss die Reihenfolge der Ausführung der Anweisungen gleich sein. Andernfalls funktionieren komplexere asynchrone Systeme möglicherweise nicht zuverlässig.
Ein weiteres schlechtes Design Wahl für Implementierungen in anderen Sprachen, die mehrere simultane Ausführungskontexte haben - sagen wir eine Multi-Threaded-Umgebung (irrelevant in JavaScript, da nur ein Thread der Ausführung ist) ist, dass die Fortsetzung synchron aufgerufen wird in Bezug auf die resolve
Funktion. Dies ist sogar problematisch, wenn die asynchrone Task in einem späteren Ereignisschleifenzyklus endet und somit die Fortsetzung asynchron in Bezug auf die Call-Site ausgeführt wird.
Wenn jedoch die resolve
Funktion wird durch die asynchrone Aufgabe aufgerufen werden, wenn es fertig ist, kann diese Aufgabe auf einen privaten Ausführungskontext auszuführen (sagt den „Worker-Thread“).Dieser "Worker-Thread" ist normalerweise ein dedizierter und möglicherweise speziell konfigurierter Ausführungskontext - der dann resolve
aufruft. Wenn die resolve
Funktion synchron die Fortsetzung ausführen wird, wird die Fortsetzung auf dem privaten Ausführungskontext der Aufgabe ausgeführt - was im Allgemeinen nicht gewünscht ist.
Wenn Sie möchten, dass ein Versprechen sofort aufgelöst wird, rufen Sie einfach 'promise.resolve()' auf und es ruft zu diesem Zeitpunkt alle registrierten Lösungshandler auf. Wenn Sie möchten, dass sich Ihre eigene Ereignisschleife auflöst, bevor das Versprechen aufgelöst wird, können Sie 'setTimeout (function() {provive.resolve()}, 10);'. Aber es hängt davon ab, wie das '.resolve()' sich verhalten soll. Es ist nicht erforderlich, 'setTimeout()' zu verwenden. – jfriend00
Ja, das verstehe ich. Was ich verstehen möchte ist, wenn setTimeout verwendet wird, wird foo() nicht sofort nach 10 Millisekunden aufgerufen, sondern die Auflösung wird in eine Queue gestellt und foo() wird später ausgeführt. – silentorb
Welches Versprechen verspricht die Umsetzung? Da es viele verschiedene Implementierungen gibt, ist es schwierig, eine solche Frage zu stellen, ohne sich auf eine bestimmte Implementierung zu beziehen. Soweit ich weiß, gibt es keine Spezifikation, die besagt, dass Rückrufe erst nach einer Verzögerung aufgerufen werden sollten. – jfriend00