Dies ist eine coole Frage, um auf den Grund zu gehen.
Wenn Sie dies tun:
verifier(3,4).then(...)
Dadurch wird ein neues Versprechen zurückgegeben, für das ein weiterer Zyklus zur Ereignisschleife erforderlich ist, bevor dieses neu abgelehnte Versprechen den folgenden .catch()
Handler ausführen kann . Dieser zusätzliche Zyklus ergibt die nächste Sequenz:
verifier(5,4).then(...)
Es besteht die Möglichkeit, den .then()
Handler vor der vorherigen Zeile auszuführen , .catch()
da er sich bereits in der Warteschlange befand, bevor der .catch()
Handler der ersten Zeile in die Warteschlange gelangt und Elemente in der FIFO-Reihenfolge aus der Warteschlange ausgeführt werden.
Beachten Sie, dass das .then(f1, f2)
Formular , wenn Sie es anstelle von verwenden .then().catch()
, ausgeführt wird, wenn Sie es erwarten, da es kein zusätzliches Versprechen und somit kein zusätzliches Häkchen enthält:
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response (3,4): ", response),
(error) => console.log("error (3,4): ", error)
);
verifier(5, 4)
.then((response) => console.log("response (5,4): ", response))
.catch((error) => console.log("error (5,4): ", error));
Beachten Sie, dass ich auch alle Nachrichten beschriftet habe, damit Sie sehen können, von welchem verifier()
Anruf sie kommen, was das Lesen der Ausgabe erheblich erleichtert.
ES6-Spezifikation zur Bestellung von Rückrufversprechen und ausführlichere Erläuterungen
Die ES6-Spezifikation sagt uns, dass Versprechen "Jobs" (wie es einen Rückruf von einem .then()
oder aufruft .catch()
) in FIFO-Reihenfolge ausgeführt werden, basierend darauf, wann sie in die Jobwarteschlange eingefügt werden. FIFO wird nicht speziell benannt, es wird jedoch angegeben, dass neue Jobs am Ende der Warteschlange eingefügt werden und Jobs ab dem Anfang der Warteschlange ausgeführt werden. Das implementiert die FIFO-Reihenfolge.
PerformPromiseThen (das den Rückruf von ausführt .then()
) führt zu EnqueueJob. Auf diese Weise wird geplant, dass der Auflösungs- oder Ablehnungshandler tatsächlich ausgeführt wird. EnqueueJob gibt an, dass der ausstehende Job am Ende der Jobwarteschlange hinzugefügt wird. Dann zieht die NextJob- Operation das Element von der Vorderseite der Warteschlange. Dies stellt die FIFO-Reihenfolge bei der Bearbeitung von Jobs aus der Promise-Jobwarteschlange sicher.
Im Beispiel in der ursprünglichen Frage erhalten wir die Rückrufe für das verifier(3,4)
Versprechen und das verifier(5,4)
Versprechen, die in der Reihenfolge, in der sie ausgeführt wurden, in die Jobwarteschlange eingefügt wurden, da beide ursprünglichen Versprechen erfüllt sind. Wenn der Interpreter dann zur Ereignisschleife zurückkehrt, nimmt er zuerst den verifier(3,4)
Job auf. Dieses Versprechen wird abgelehnt und es gibt keinen Rückruf dafür in der verifier(3,4).then(...)
. Es wird also das zurückgegebene Versprechen abgelehnt, das dazu führt verifier(3,4).then(...)
, dass der verifier(3,4).then(...).catch(...)
Handler in die jobQueue eingefügt wird.
Dann kehrt es zur Ereignisschleife zurück und der nächste Job, den es aus der jobQueue abruft, ist der verifier(5, 4)
Job. Das hat ein gelöstes Versprechen und einen entschlossenen Handler, also nennt es diesen Handler. Dadurch wird die response (5,4):
Ausgabe angezeigt.
Dann kehrt es zur Ereignisschleife zurück und der nächste Job, den es aus der jobQueue abruft, ist der verifier(3,4).then(...).catch(...)
Job, bei dem es ausgeführt wird, und dies verursacht dieerror (3,4)
Ausgabe angezeigt.
Dies liegt daran, dass die .catch()
in der 1. Kette eine Versprechungsstufe tiefer in ihrer Kette liegt als die .then()
in der 2. Kette, die die von Ihnen gemeldete Bestellung verursacht. Dies liegt daran, dass Versprechen-Ketten nicht synchron über die Job-Warteschlange in FIFO-Reihenfolge von einer Ebene zur nächsten durchlaufen werden.
Allgemeine Empfehlung, sich auf diese Ebene der Planungsdetails zu verlassen
Zu Ihrer Information, im Allgemeinen versuche ich, Code zu schreiben, der nicht von diesem Grad an detailliertem Timing-Wissen abhängt. Es ist zwar neugierig und gelegentlich nützlich zu verstehen, aber es ist fragiler Code, da eine einfache, scheinbar harmlose Änderung des Codes zu einer Änderung des relativen Timings führen kann. Wenn das Timing zwischen zwei Ketten wie dieser kritisch ist, würde ich den Code lieber so schreiben, dass das Timing so erzwungen wird, wie ich es möchte, anstatt mich auf dieses detaillierte Verständnis zu verlassen.