Ich mache einige Unit-Tests. Das Testframework lädt eine Seite in einen iFrame und führt dann Zusicherungen für diese Seite aus. Bevor jeder Test beginnt, erstelle ich ein Ereignis, Promise
das das aufzurufende onload
Ereignis resolve()
des iFrames festlegt, das des iFrames festlegt src
und das Versprechen zurückgibt.
Ich kann also einfach anrufen loadUrl(url).then(myFunc)
und es wird warten, bis die Seite geladen ist, bevor etwas ausgeführt myFunc
wird.
Ich verwende diese Art von Muster überall in meinen Tests (nicht nur zum Laden von URLs), hauptsächlich, um Änderungen am DOM zuzulassen (z. B. das Klicken auf eine Schaltfläche nachahmen und darauf warten, dass Divs ausgeblendet und angezeigt werden).
Der Nachteil dieses Designs ist, dass ich ständig anonyme Funktionen mit ein paar Codezeilen schreibe. Während ich eine Problemumgehung (QUnit assert.async()
) habe, wird die Testfunktion, die die Versprechen definiert, abgeschlossen, bevor das Versprechen ausgeführt wird.
Ich frage mich, ob es eine Möglichkeit gibt, einen Wert von a Promise
abzurufen oder zu warten (block / sleep), bis er aufgelöst ist, ähnlich wie bei .NET IAsyncResult.WaitHandle.WaitOne()
. Ich weiß, dass JavaScript Single-Threaded ist, aber ich hoffe, dass dies nicht bedeutet, dass eine Funktion nicht nachgeben kann.
Gibt es im Wesentlichen eine Möglichkeit, die folgenden Ergebnisse in der richtigen Reihenfolge auszuspucken?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
quelle
.then(fnAppend.bind(myDiv))
z. B. , die Anonen erheblich reduzieren können.Antworten:
Die aktuelle Generation von Javascript in Browsern verfügt nicht über ein
wait()
odersleep()
, mit dem andere Dinge ausgeführt werden können. Sie können also einfach nicht tun, was Sie verlangen. Stattdessen hat es asynchrone Operationen, die ihre Sache erledigen und Sie dann anrufen, wenn sie fertig sind (wie Sie es für Versprechen verwendet haben).Ein Teil davon ist auf die Single-Threadedness von Javascript zurückzuführen. Wenn sich der einzelne Thread dreht, kann kein anderes Javascript ausgeführt werden, bis dieser sich drehende Thread fertig ist. ES6 führt
yield
und generiert Generatoren, die solche kooperativen Tricks ermöglichen, aber wir sind weit davon entfernt, diese in einer Vielzahl installierter Browser zu verwenden (sie können in einigen serverseitigen Entwicklungen verwendet werden, in denen Sie die JS-Engine steuern das wird verwendet).Eine sorgfältige Verwaltung von versprechungsbasiertem Code kann die Ausführungsreihenfolge für viele asynchrone Vorgänge steuern.
Ich bin mir nicht sicher, ob ich genau verstehe, welche Reihenfolge Sie in Ihrem Code erreichen möchten, aber Sie könnten so etwas mit Ihrer vorhandenen
kickOff()
Funktion tun und dann.then()
nach dem Aufrufen einen Handler daran anhängen :Dadurch wird die Ausgabe in einer garantierten Reihenfolge zurückgegeben - wie folgt:
Update im Jahr 2018 (drei Jahre nach dieser Antwort):
Wenn Sie Ihren Code entweder transpilieren oder in einer Umgebung ausführen, die ES7-Funktionen wie
async
und unterstütztawait
, können Sieawait
Ihren Code jetzt "erscheinen" lassen, um auf das Ergebnis eines Versprechens zu warten. Es entwickelt sich immer noch mit Versprechen. Es blockiert immer noch nicht das gesamte Javascript, aber es ermöglicht Ihnen, sequentielle Operationen in einer freundlicheren Syntax zu schreiben.Anstelle der ES6-Methode:
Du kannst das:
quelle
then()
ist, was ich getan habe. Ich mag es einfach nicht, diefunction() { ... }
ganze Zeit schreiben zu müssen . Es überfrachtet den Code.(foo) => { ... }
stattfunction(foo) { ... }
foo => single.expression(here)
, um geschweifte und runde Klammern zu entfernen .async
undawait
Syntax als Option aktualisiert, die jetzt in node.js und in modernen Browsern oder über Transpiler verfügbar ist.Wenn ES2016 verwenden , können Sie verwenden ,
async
undawait
und so etwas wie:Wenn Sie ES2015 verwenden, können Sie Generatoren verwenden . Wenn Sie nicht wie die Syntax tun abstrakt Sie können es mit entfernt eine
async
Nutzenfunktion wie hier erläutert .Wenn Sie ES5 verwenden, möchten Sie wahrscheinlich eine Bibliothek wie Bluebird , die Ihnen mehr Kontrolle gibt.
Wenn Ihre Laufzeit ES2015 unterstützt, kann die bereits ausgeführte Ausführungsreihenfolge mithilfe von Fetch Injection parallel beibehalten werden .
quelle
async
/ transpilieren könnenawait
.async
/await
ist nur eine andere Syntax fürPromise.then
.async
, warten Sie nicht ... es sei denn, es gibt Verwendungawait
. Das ES2016 / ES7-Beispiel kann immer noch nicht SO ausgeführt werden. Hier ist ein Code, den ich vor drei Jahren geschrieben habe, um das Verhalten zu demonstrieren.Eine andere Option ist die Verwendung von Promise.all, um auf die Auflösung einer Reihe von Versprechungen zu warten. Der folgende Code zeigt, wie Sie warten müssen, bis alle Versprechen gelöst sind, und dann mit den Ergebnissen umgehen, sobald sie alle fertig sind (da dies das Ziel der Frage zu sein schien). Start könnte jedoch offensichtlich ausgegeben werden, bevor die Mitte aufgelöst wurde, indem dieser Code hinzugefügt wird, bevor die Auflösung aufgerufen wird.
quelle
Sie können es manuell tun. (Ich weiß, dass das keine gute Lösung ist, aber ..) benutze
while
loop, bis derresult
keinen Wert mehr hatquelle