Zusammenfassung: Gibt es einige bewährte Best-Practice-Muster, die ich befolgen kann, um meinen Code trotz der Verwendung von asynchronem Code und Rückrufen lesbar zu halten?
Ich verwende eine JavaScript-Bibliothek, die viele Dinge asynchron erledigt und stark von Rückrufen abhängt. Es scheint, dass das Schreiben einer einfachen Methode "Laden A, Laden B, ..." ziemlich kompliziert und mit diesem Muster schwer zu befolgen ist.
Lassen Sie mich ein (erfundenes) Beispiel geben. Angenommen, ich möchte eine Reihe von Bildern (asynchron) von einem Remote-Webserver laden. In C # / async würde ich so etwas schreiben:
disableStartButton();
foreach (myData in myRepository) {
var result = await LoadImageAsync("http://my/server/GetImage?" + myData.Id);
if (result.Success) {
myData.Image = result.Data;
} else {
write("error loading Image " + myData.Id);
return;
}
}
write("success");
enableStartButton();
Das Codelayout folgt dem "Ablauf der Ereignisse": Zuerst wird die Startschaltfläche deaktiviert, dann werden die Bilder geladen ( await
stellt sicher, dass die Benutzeroberfläche reagiert) und dann wird die Startschaltfläche wieder aktiviert.
In JavaScript habe ich mithilfe von Rückrufen Folgendes gefunden:
disableStartButton();
var count = myRepository.length;
function loadImage(i) {
if (i >= count) {
write("success");
enableStartButton();
return;
}
myData = myRepository[i];
LoadImageAsync("http://my/server/GetImage?" + myData.Id,
function(success, data) {
if (success) {
myData.Image = data;
} else {
write("error loading image " + myData.Id);
return;
}
loadImage(i+1);
}
);
}
loadImage(0);
Ich denke, die Nachteile liegen auf der Hand: Ich musste die Schleife in einen rekursiven Aufruf umwandeln, der Code, der am Ende ausgeführt werden soll, befindet sich irgendwo in der Mitte der Funktion, der Code, der den Download ( loadImage(0)
) startet, befindet sich ganz unten. und es ist im Allgemeinen viel schwieriger zu lesen und zu folgen. Es ist hässlich und ich mag es nicht.
Ich bin mir sicher, dass ich nicht der erste bin, der auf dieses Problem stößt. Meine Frage lautet daher: Gibt es einige bewährte Best-Practice-Muster, die ich befolgen kann, um meinen Code trotz der Verwendung von asynchronem Code und Rückrufen lesbar zu halten?
LoadImageAsync
in der Tat ein AufrufExt.Ajax.request
von Sencha Touch.async.waterfall
ist Ihre Antwort.Antworten:
Es ist höchst unwahrscheinlich, dass Sie mit einfachen js das gleiche Maß an Prägnanz und Ausdruckskraft erreichen können, wenn Sie mit Rückrufen arbeiten, die C # 5 hat. Der Compiler erledigt die Arbeit, indem er das gesamte Boilerplate für Sie schreibt, und bis die js-Laufzeiten dies tun, müssen Sie hier und da noch einen gelegentlichen Rückruf durchführen.
Möglicherweise möchten Sie Rückrufe jedoch nicht immer auf das Niveau der Einfachheit von linearem Code reduzieren - das Herumwerfen von Funktionen muss nicht hässlich sein, es gibt eine ganze Welt, die mit dieser Art von Code arbeitet, und sie bleiben ohne
async
und vernünftigawait
.Verwenden Sie zum Beispiel Funktionen höherer Ordnung (mein js kann etwas rostig sein):
quelle
Ich habe ein bisschen gebraucht, um zu entschlüsseln, warum du es so machst, aber ich denke, das könnte nahe an dem liegen, was du willst?
Zunächst werden so viele AJAX-Anforderungen wie möglich gleichzeitig ausgeführt, und es wird gewartet, bis alle abgeschlossen sind, bevor die Schaltfläche Start aktiviert wird. Dies ist schneller als ein sequentielles Warten und meiner Meinung nach viel einfacher zu befolgen.
BEARBEITEN : Beachten Sie, dass dies voraussetzt, dass Sie
.each()
verfügbar sind, und dass diesmyRepository
ein Array ist. Achten Sie darauf, welche Schleifeniteration Sie hier anstelle dieser verwenden, falls diese nicht verfügbar ist. Diese nutzt die Schließungseigenschaften für den Rückruf. Ich bin mir jedoch nicht sicher, was Sie zur Verfügung haben, daLoadImageAsync
es Teil einer spezialisierten Bibliothek zu sein scheint - ich sehe keine Ergebnisse in Google.quelle
.each()
verfügbar, und jetzt, da Sie es erwähnen, ist es nicht unbedingt erforderlich, das Laden nacheinander durchzuführen. Ich werde auf jeden Fall Ihre Lösung ausprobieren. (Obwohl ich die Antwort von vski akzeptieren werde, da sie näher an der ursprünglichen, allgemeineren Frage liegt.)Haftungsausschluss: Diese Antwort beantwortet nicht speziell Ihr Problem. Sie ist eine allgemeine Antwort auf die Frage: "Gibt es einige bewährte Best-Practice-Muster, die ich befolgen kann, um meinen Code trotz Verwendung von asynchronem Code und Rückrufen lesbar zu halten?"
Soweit ich weiß, gibt es kein "gut etabliertes" Muster, um damit umzugehen. Ich habe jedoch zwei Arten von Methoden gesehen, um die verschachtelten Rückruf-Albträume zu vermeiden.
1 / Benannte Funktionen anstelle von anonymen Rückrufen verwenden
Auf diese Weise vermeiden Sie die Verschachtelung, indem Sie die Logik der anonymen Funktion in einer anderen Funktion senden.
2 / Verwenden einer Flow Management-Bibliothek. Ich benutze gerne Step , aber es ist nur eine Frage der Präferenz. Es ist übrigens das, was LinkedIn verwendet.
Ich verwende eine Flow-Management-Bibliothek, wenn ich viele verschachtelte Rückrufe verwende, da diese viel lesbarer ist, wenn viel Code verwendet wird.
quelle
Einfach ausgedrückt, JavaScript hat nicht den syntaktischen Zucker von
await
.Das Verschieben des "End" -Teils in den unteren Bereich der Funktion ist jedoch einfach. und mit einer sofort ausgeführten anonymen Funktion können wir vermeiden, einen Verweis darauf zu deklarieren.
Sie können den Teil "end" auch als Erfolgsrückruf an die Funktion übergeben.
quelle