Bei der asynchronen Single-Thread-Programmierung gibt es zwei Haupttechniken, mit denen ich vertraut bin. Am häufigsten werden Rückrufe verwendet. Das bedeutet, dass an die Funktion übergeben wird, die asynchron eine Rückruffunktion als Parameter ausführt. Wenn der asynchrone Vorgang abgeschlossen ist, wird der Rückruf aufgerufen.
Ein typischer jQuery
Code, der auf diese Weise entworfen wurde:
$.get('userDetails', {'name': 'joe'}, function(data) {
$('#userAge').text(data.age);
});
Diese Art von Code kann jedoch unübersichtlich und stark verschachtelt werden, wenn nach Abschluss des vorherigen Codes nacheinander weitere asynchrone Aufrufe ausgeführt werden sollen.
Ein zweiter Ansatz ist die Verwendung von Versprechen. Ein Promise ist ein Objekt, das einen Wert darstellt, der möglicherweise noch nicht existiert. Sie können Rückrufe festlegen, die aufgerufen werden, wenn der Wert zum Lesen bereit ist.
Der Unterschied zwischen Promises und dem herkömmlichen Callback-Ansatz besteht darin, dass asynchrone Methoden jetzt synchron Promise-Objekte zurückgeben, auf die der Client einen Callback setzt. Zum Beispiel ähnlicher Code mit Promises in AngularJS:
$http.get('userDetails', {'name': 'joe'})
.then(function(response) {
$('#userAge').text(response.age);
});
Meine Frage lautet also: Gibt es tatsächlich einen wirklichen Unterschied? Der Unterschied scheint rein syntaktisch zu sein.
Gibt es einen tieferen Grund, eine Technik über der anderen anzuwenden?
Antworten:
Es ist fair zu sagen, dass Versprechen nur syntaktischer Zucker sind. Alles, was Sie mit Versprechungen tun können, können Sie mit Rückrufen tun. Tatsächlich bieten die meisten vielversprechenden Implementierungen Möglichkeiten zur Konvertierung zwischen den beiden, wann immer Sie möchten.
Der tiefe Grund, warum Versprechen oft besser sind, ist, dass sie komponierbarer sind , was ungefähr bedeutet, dass das Kombinieren mehrerer Versprechen "einfach funktioniert", während das Kombinieren mehrerer Rückrufe oft nicht funktioniert. Zum Beispiel ist es trivial, einer Variablen ein Versprechen zuzuweisen und später zusätzliche Handler hinzuzufügen, oder sogar einen Handler an eine große Gruppe von Versprechungen anzuhängen, die erst ausgeführt werden, nachdem alle Versprechungen aufgelöst wurden. Während Sie diese Dinge mit Rückrufen emulieren können, ist viel mehr Code erforderlich , es ist sehr schwierig, sie korrekt auszuführen , und das Endergebnis ist in der Regel weitaus weniger wartbar.
Eine der größten (und subtilsten) Möglichkeiten, wie Versprechungen komponiert werden können, ist die einheitliche Behandlung von Rückgabewerten und nicht erfassten Ausnahmen. Wie eine Ausnahme bei Rückrufen behandelt wird, hängt möglicherweise ganz davon ab, welche der vielen verschachtelten Rückrufe sie ausgelöst hat und welche der Funktionen, die Rückrufe entgegennehmen, einen Try / Catch in ihrer Implementierung aufweist. Mit Versprechungen wissen Sie , dass eine Ausnahme, die einer Rückruffunktion entgeht, abgefangen und an den Fehlerhandler übergeben wird, den Sie mit
.error()
oder bereitgestellt haben.catch()
.Für das Beispiel, das Sie für einen einzelnen Rückruf gegenüber einem einzelnen Versprechen gegeben haben, gibt es zwar keinen signifikanten Unterschied. Es ist, wenn Sie eine Unmenge von Rückrufen im Vergleich zu einer Unmenge von Versprechungen haben, dass der Code, der auf Versprechungen basiert, tendenziell viel besser aussieht.
Hier ist ein Versuch mit einem hypothetischen Code, der mit Versprechungen und Rückrufen geschrieben wurde. Er sollte nur so komplex sein, dass Sie eine Vorstellung davon haben, wovon ich spreche.
Mit Versprechen:
Mit Rückrufen:
Es gibt vielleicht einige clevere Möglichkeiten, die Code-Duplizierung in der Callback-Version zu reduzieren, auch ohne Versprechen, aber alle, die ich mir vorstellen kann, laufen darauf hinaus, etwas sehr vielversprechendes zu implementieren.
quelle
yield
ed-Versprechungen zurückgibt, einer weiteren "Zuckerung" zugänglich sind . Der Vorteil hierbei ist, dass Sie die Möglichkeit haben, systemeigene Kontrollflussstrukturen zu mischen, die sich in der Anzahl der von ihnen ausgeführten asynchronen Vorgänge unterscheiden können. Ich werde eine Version hinzufügen, die dies zeigt.then(callback)
Promise-Methode, die einen Rückruf akzeptiert (anstelle einer API-Methode, die diesen Rückruf akzeptiert), muss nichts mit IoC zu tun haben. Promise führt eine Indirektionsebene ein, die für die Komposition, Verkettung und Fehlerbehandlung nützlich ist (in der Tat eisenbahnorientierte Programmierung), aber Callback wird immer noch nicht vom Client ausgeführt, so dass IoC nicht wirklich fehlt.