Ich habe nicht viele Ressourcen dazu gefunden: Ich habe mich gefragt, ob es möglich / eine gute Idee ist, asynchronen Code synchron schreiben zu können.
Hier ist zum Beispiel ein JavaScript-Code, der die Anzahl der in einer Datenbank gespeicherten Benutzer abruft (eine asynchrone Operation):
getNbOfUsers(function (nbOfUsers) { console.log(nbOfUsers) });
Es wäre schön, so etwas schreiben zu können:
const nbOfUsers = getNbOfUsers();
console.log(getNbOfUsers);
Und so würde der Compiler automatisch auf die Antwort warten und dann ausführen console.log
. Es wird immer darauf gewartet, dass die asynchronen Vorgänge abgeschlossen sind, bevor die Ergebnisse an einer anderen Stelle verwendet werden müssen. Wir würden Rückrufversprechen, asynchrone / erwartete oder was auch immer, viel weniger nutzen und uns niemals Sorgen machen müssen, ob das Ergebnis einer Operation sofort verfügbar ist oder nicht.
Fehler wären nbOfUsers
mit try / catch oder ähnlichen Optionen wie in der Swift- Sprache immer noch beherrschbar (haben Sie eine Ganzzahl oder einen Fehler erhalten?) .
Ist es möglich? Es kann eine schreckliche Idee / eine Utopie sein ... Ich weiß es nicht.
await
saTask<T>
es zu konvertierenT
async
/await
, wodurch die asynchronen Teile der Ausführung explizit werden.Antworten:
Async / await ist genau das automatisierte Management, das Sie vorschlagen, allerdings mit zwei zusätzlichen Schlüsselwörtern. Warum sind sie wichtig? Abgesehen von der Abwärtskompatibilität?
Ohne explizite Punkte, an denen eine Coroutine ausgesetzt und wieder aufgenommen werden kann, benötigen wir ein Typsystem, um festzustellen, wo ein erwarteter Wert erwartet werden muss. Viele Programmiersprachen haben kein solches Typensystem.
Indem wir das Warten auf einen Wert explizit machen, können wir auch erwartbare Werte als erstklassige Objekte weitergeben: Versprechen. Dies kann beim Schreiben von Code höherer Ordnung sehr nützlich sein.
Asynchroner Code hat sehr tiefe Auswirkungen auf das Ausführungsmodell einer Sprache, ähnlich wie das Fehlen oder Vorhandensein von Ausnahmen in der Sprache. Insbesondere kann eine Async-Funktion nur von Async-Funktionen erwartet werden. Dies betrifft alle aufrufenden Funktionen! Was aber, wenn wir eine Funktion am Ende dieser Abhängigkeitskette von nicht asynchron auf asynchron ändern? Dies wäre eine rückwärts inkompatible Änderung ... es sei denn, alle Funktionen sind asynchron und jeder Funktionsaufruf wird standardmäßig abgewartet.
Und das ist höchst unerwünscht, weil es sehr schlechte Auswirkungen auf die Leistung hat. Sie könnten nicht einfach günstige Werte zurückgeben. Jeder Funktionsaufruf würde viel teurer werden.
Async ist großartig, aber eine Art impliziter Async funktioniert in der Realität nicht.
Reine funktionale Sprachen wie Haskell haben eine gewisse Notlösung, da die Ausführungsreihenfolge weitgehend unbestimmt und nicht beobachtbar ist. Oder anders formuliert: Jede bestimmte Reihenfolge von Operationen muss explizit codiert werden. Dies kann für reale Programme ziemlich umständlich sein, insbesondere für Programme mit hohem E / A-Aufwand, für die Async-Code sehr gut geeignet ist.
quelle
someValue ifItIsAFuture [self| self messageIWantToSend]
weil es schwierig ist, mit generischem Code zu integrieren.par
im reinen Haskell-Code so ziemlich überall hinwerfen und kostenlos Paralellismus erhalten können.Was Sie vermissen, ist der Zweck von asynchronen Operationen: Sie ermöglichen es Ihnen, Ihre Wartezeit zu nutzen!
Wenn Sie eine asynchrone Operation wie das Anfordern einer Ressource von einem Server in eine synchrone Operation umwandeln, indem Sie implizit und sofort auf die Antwort warten, kann Ihr Thread die Wartezeit nicht ändern . Wenn der Server 10 Millisekunden benötigt, um zu antworten, gehen ungefähr 30 Millionen CPU-Zyklen in den Abfall. Die Wartezeit der Antwort wird zur Ausführungszeit für die Anforderung.
Der einzige Grund, warum Programmierer asynchrone Operationen erfunden haben, besteht darin, die Latenz von inhärent lang laufenden Aufgaben hinter anderen nützlichen Berechnungen zu verbergen . Wenn Sie die Wartezeit mit nützlicher Arbeit füllen können, ist das CPU-Zeit gespart. Wenn dies nicht möglich ist, geht nichts durch die asynchrone Operation verloren.
Daher empfehle ich, die von Ihren Sprachen bereitgestellten asynchronen Vorgänge zu nutzen. Sie sind da, um Ihnen Zeit zu sparen.
quelle
Einige tun.
Sie sind (noch) kein Mainstream, weil Async ein relativ neues Feature ist, für das wir erst jetzt ein gutes Gefühl bekommen haben, ob es überhaupt ein gutes Feature ist oder wie man es Programmierern auf eine Art und Weise präsentiert, die freundlich / benutzbar / ausdrucksstark / etc. Vorhandene asynchrone Funktionen sind weitgehend an vorhandene Sprachen gebunden, was einen etwas anderen Entwurfsansatz erfordert.
Trotzdem ist es nicht unbedingt eine gute Idee, überall etwas zu unternehmen. Ein häufiger Fehler besteht darin, asynchrone Aufrufe in einer Schleife auszuführen und ihre Ausführung effektiv zu serialisieren. Implizite asynchrone Aufrufe können diese Art von Fehlern verschleiern. Wenn Sie implizite Nötigung von a
Task<T>
(oder einer Entsprechung Ihrer Sprache) zuT
unterstützen, kann dies zu einer gewissen Komplexität / Kosten für Ihre Typechecker- und Fehlerberichterstattung führen, wenn nicht klar ist, welche der beiden vom Programmierer wirklich gewünscht wurde.Das sind aber keine unüberwindlichen Probleme. Wenn Sie dieses Verhalten unterstützen wollten, könnten Sie es mit ziemlicher Sicherheit, obwohl es Kompromisse geben würde.
quelle
Es gibt Sprachen, die dies tun. Es besteht jedoch eigentlich kein großer Bedarf, da dies mit vorhandenen Sprachfunktionen leicht erreicht werden kann.
Solange Sie haben eine gewisse Art und Weise auszudrücken Asynchronität, können Sie implementieren Futures oder Versprechungen rein als Bibliothek Feature, Sie keine speziellen Sprachfunktionen benötigen. Und solange Sie haben einige auszudrücken Transparente Proxies , können Sie die beiden Funktionen zusammen , und Sie haben Transparent Futures .
Beispielsweise kann in Smalltalk und seinen Nachkommen ein Objekt seine Identität ändern, es kann buchstäblich ein anderes Objekt werden (und tatsächlich wird die Methode, die dies ausführt, aufgerufen
Object>>become:
).Stellen Sie sich eine lang andauernde Berechnung vor, die a zurückgibt
Future<Int>
. DiesFuture<Int>
hat alle die gleichen MethodenInt
, außer mit unterschiedlichen Implementierungen.Future<Int>
Bei der+
Methode von wird keine weitere Zahl hinzugefügt und das Ergebnis zurückgegeben. Es wird eine neue zurückgegeben,Future<Int>
die die Berechnung umschließt. Und so weiter und so fort. Methoden, die durch Rückgabe von a nicht sinnvoll implementiert werden könnenFuture<Int>
, werden stattdessen automatischawait
das Ergebnis und anschließend der Aufrufself become: result.
, der das aktuell ausgeführte Objekt (self
dhFuture<Int>
dasresult
Objekt ) buchstäblich zum Objekt macht, dh von nun an die Objektreferenz, die früher einFuture<Int>
ist jetzt einInt
überall, völlig transparent für den Kunden.Es sind keine speziellen asynchronen Sprachfunktionen erforderlich.
quelle
Future<T>
undT
einige gemeinsame Schnittstelle und ich Funktionen von dieser Schnittstelle verwenden. Soll esbecome
das Ergebnis sein und dann die Funktionalität nutzen, oder nicht? Ich denke an Dinge wie einen Gleichheitsoperator oder eine Debug-Darstellung in Form eines To-Strings.a + b
beiden ganzen Zahlen spielt es keine Rolle, ob a und b sofort oder später verfügbar sind. Wir schreiben einfacha + b
(machen es möglichInt + Future<Int>
)Future<T>
undT
aus Ihrer Sicht gibt es keineFuture<T>
, nur eineT
. Jetzt gibt es natürlich viele technische Herausforderungen, wie dies effizient gestaltet werden kann, welche Vorgänge blockiert oder nicht blockiert werden sollen usw., aber das ist wirklich unabhängig davon, ob Sie es als Sprache oder als Bibliotheksfeature ausführen. Transparenz war eine Anforderung, die vom OP in der Frage festgelegt wurde. Ich werde nicht behaupten, dass es schwierig ist und möglicherweise keinen Sinn ergibt.Sie tun (nun, die meisten von ihnen). Die gesuchte Funktion heißt Threads .
Threads haben jedoch ihre eigenen Probleme:
Da der Code jederzeit ausgesetzt werden kann , können Sie niemals davon ausgehen, dass sich die Dinge nicht "von selbst" ändern. Wenn Sie mit Threads programmieren, denken Sie viel Zeit darüber nach, wie Ihr Programm mit Änderungen umgehen soll.
Stellen Sie sich vor, ein Spielserver verarbeitet den Angriff eines Spielers auf einen anderen Spieler. Etwas wie das:
Drei Monate später entdeckt ein Spieler, dass er seine Gegenstände behalten kann, wenn er getötet wird und sich
attacker.addInventoryItems
im laufenden Betriebvictim.removeInventoryItems
abmeldet. Der Angreifer erhält auch eine Kopie seiner Gegenstände. Er tut dies mehrere Male, indem er aus dem Nichts eine Million Tonnen Gold erschafft und die Wirtschaft des Spiels zum Erliegen bringt.Alternativ kann sich der Angreifer abmelden, während das Spiel eine Nachricht an das Opfer sendet, und er bekommt keinen "Mörder" -Tag über dem Kopf, damit sein nächstes Opfer nicht vor ihm davonläuft.
Da der Code jederzeit ausgesetzt werden kann , müssen Sie beim Bearbeiten von Datenstrukturen überall Sperren verwenden. Ich habe oben ein Beispiel angeführt, das offensichtliche Konsequenzen für ein Spiel hat, aber subtiler sein kann. Erwägen Sie, dem Anfang einer verknüpften Liste ein Element hinzuzufügen:
Dies ist kein Problem, wenn Sie sagen, dass Threads nur dann angehalten werden können, wenn sie E / A ausführen, und nicht zu irgendeinem Zeitpunkt. Aber ich bin sicher, Sie können sich eine Situation vorstellen, in der es eine E / A-Operation gibt - wie zum Beispiel die Protokollierung:
Da der Code kann ausgesetzt werden jedem Punkt , könnte es möglicherweise viel Staat zu retten. Das System behandelt dies, indem es jedem Thread einen völlig separaten Stapel gibt. Der Stapel ist jedoch ziemlich groß, sodass Sie in einem 32-Bit-Programm nicht mehr als etwa 2000 Threads haben können. Sie können auch die Stapelgröße reduzieren, wenn die Gefahr besteht, dass der Stapel zu klein wird.
quelle
Viele der hier gegebenen Antworten sind irreführend, da die Frage zwar buchstäblich nach asynchroner Programmierung und nicht nach blockierungsfreiem E / A gestellt wurde, wir aber in diesem speziellen Fall nicht über das eine diskutieren können, ohne das andere zu diskutieren.
Während asynchrone Programmierung von Natur aus asynchron ist, besteht das Hauptziel asynchroner Programmierung darin, das Blockieren von Kernel-Threads zu vermeiden. Node.js verwendet Asynchronität über Callbacks oder
Promise
s, um zu ermöglichen, dass Blockierungsvorgänge von einer Ereignisschleife ausgelöst werden, und Netty in Java verwendet Asynchronität über Callbacks oderCompletableFuture
s, um etwas Ähnliches zu tun.Nicht blockierender Code erfordert jedoch keine Asynchronität . Es kommt darauf an, wie viel Ihre Programmiersprache und Laufzeit bereit ist, für Sie zu tun.
Go, Erlang und Haskell / GHC können dies für Sie erledigen. Sie können so etwas wie schreiben
var response = http.get('example.com/test')
und einen Kernel-Thread hinter den Kulissen veröffentlichen lassen, während Sie auf eine Antwort warten. Dies geschieht durch Goroutinen, Erlang-Prozesse oder dasforkIO
Loslassen von Kernel-Threads hinter den Kulissen beim Blockieren, sodass andere Aktionen ausgeführt werden können, während auf eine Antwort gewartet wird .Es ist wahr, dass die Sprache nicht wirklich mit Asynchronität umgehen kann, aber einige Abstraktionen lassen Sie weiter gehen als andere, z. B. unbegrenzte Fortsetzungen oder asymmetrische Koroutinen. Die Hauptursache für asynchronen Code, das Blockieren von Systemaufrufen, kann jedoch absolut vom Entwickler entfernt werden.
Node.js und Java unterstützen asynchronen, nicht blockierenden Code, wohingegen Go und Erlang synchronen, nicht blockierenden Code unterstützen. Sie sind beide gültige Ansätze mit unterschiedlichen Kompromissen.
Mein eher subjektives Argument ist, dass diejenigen, die sich gegen Laufzeiten aussprechen, die im Auftrag des Entwicklers nicht blockieren, sich gegen die Müllabfuhr in den frühen Neunzigern aussprechen. Ja, es verursacht Kosten (in diesem Fall in erster Linie mehr Speicher), erleichtert jedoch die Entwicklung und das Debuggen und macht Codebasen robuster.
Ich persönlich würde argumentieren, dass asynchroner, nicht blockierender Code in Zukunft für die Systemprogrammierung reserviert werden sollte und modernere Technologie-Stacks für die Anwendungsentwicklung auf synchrone, nicht blockierende Laufzeiten migriert werden sollten .
quelle
waitpid(..., WNOHANG)
fehlschlägt, wenn sie blockieren müsste. Oder bedeutet "synchron" hier "es gibt keine vom Programmierer sichtbaren Rückrufe / Zustandsautomaten / Ereignisschleifen"? Aber für Ihr Go-Beispiel muss ich immer noch explizit auf ein Ergebnis einer Goroutine warten, indem ich aus einem Kanal lese, nicht wahr? Wie ist das weniger asynchron als asynchron / wait in JS / C # / Python?Wenn ich Sie richtig verstehe, fordern Sie ein synchrones Programmiermodell, aber eine hochperformante Implementierung. Wenn das stimmt, steht uns das bereits in Form von grünen Fäden oder Prozessen von zB Erlang oder Haskell zur Verfügung. Also ja, es ist eine hervorragende Idee, aber die Nachrüstung bestehender Sprachen kann nicht immer so reibungslos vonstatten gehen, wie Sie es möchten.
quelle
Ich schätze die Frage und finde, dass die Mehrheit der Antworten lediglich den Status Quo verteidigt. Im Spektrum der Sprachen auf niedriger bis hoher Ebene stecken wir seit einiger Zeit in Schwierigkeiten. Die nächsthöhere Ebene wird eindeutig eine Sprache sein, die sich weniger auf die Syntax konzentriert (die Notwendigkeit expliziter Schlüsselwörter wie wait und async) und viel mehr auf die Absicht. (Offensichtliche Anerkennung an Charles Simonyi, aber ich denke an 2019 und die Zukunft.)
Wenn ich einem Programmierer erzähle, dass er Code schreiben soll, der einfach einen Wert aus einer Datenbank abruft, können Sie davon ausgehen, dass ich meine, "und BTW, hängen Sie die Benutzeroberfläche nicht auf" und "keine anderen Überlegungen einführen, die schwer zu findende Fehler überdecken ". Programmierer der Zukunft, die über eine neue Generation von Sprachen und Werkzeugen verfügen, werden sicherlich in der Lage sein, Code zu schreiben, der einfach einen Wert in einer Codezeile abruft und von dort aus weitergeht.
Die Sprache auf höchster Ebene ist Englisch, und Sie können sich auf die Kompetenz des Auftraggebers verlassen, um zu wissen, was Sie wirklich tun möchten. (Denken Sie an den Computer in Star Trek oder fragen Sie Alexa.) Wir sind weit davon entfernt, aber nähern uns dem, und ich gehe davon aus, dass die Sprache / der Compiler mehr dazu geeignet sein könnte, robusten, beabsichtigten Code zu generieren, ohne dies zu tun AI brauchen.
Einerseits gibt es neuere visuelle Sprachen wie Scratch, die dies tun und nicht mit allen syntaktischen Techniken überfordert sind. Natürlich wird viel hinter den Kulissen gearbeitet, damit sich der Programmierer keine Sorgen machen muss. Das heißt, ich schreibe keine Business-Class-Software in Scratch. Daher gehe ich wie Sie davon aus, dass es an der Zeit ist, dass ausgereifte Programmiersprachen das Synchron- / Asynchron-Problem automatisch lösen.
quelle
Das Problem, das Sie beschreiben, ist zweifach.
Es gibt ein paar Möglichkeiten, dies zu erreichen, aber im Grunde läuft es darauf hinaus
foo(4, 7, bar, quux)
.Für (1) fasse ich mehrere Prozesse zusammen, führe mehrere Kernel-Threads und Green-Thread-Implementierungen aus, die Threads auf Sprachlaufzeitebene für Kernel-Threads planen. Aus der Sicht des Problems sind sie gleich. In dieser Welt gibt keine Funktion jemals die Kontrolle aus der Perspektive ihres Threads auf oder verliert sie . Der Thread selbst hat manchmal keine Kontrolle und läuft manchmal nicht, aber Sie geben die Kontrolle über Ihren eigenen Thread in dieser Welt nicht auf. Ein System, das diesem Modell entspricht, kann möglicherweise neue Threads erzeugen oder vorhandene Threads verbinden. Ein System, das zu diesem Modell passt, ist möglicherweise nicht in der Lage, einen Thread wie den von Unix zu duplizieren
fork
.(2) ist interessant. Um dem gerecht zu werden, müssen wir über Einführungs- und Ausscheidungsformulare sprechen.
Ich werde zeigen, warum implizit
await
nicht auf abwärtskompatible Weise zu einer Sprache wie Javascript hinzugefügt werden kann. Die Grundidee ist, dass Javascript durch die Offenlegung von Versprechungen für den Benutzer und die Unterscheidung zwischen synchronen und asynchronen Kontexten ein Implementierungsdetail preisgibt, das die einheitliche Behandlung synchroner und asynchroner Funktionen verhindert. Es gibt auch die Tatsache, dass Sie keinawait
Versprechen außerhalb eines Körpers mit asynchroner Funktion geben können. Diese Entwurfswahlen sind nicht kompatibel mit "Asynchronität für den Aufrufer unsichtbar machen".Sie können eine synchrone Funktion mit einem Lambda einführen und mit einem Funktionsaufruf beseitigen.
Einführung in die Synchronfunktion:
Synchrone Funktionsbeseitigung:
Sie können dies mit der Einführung und Beseitigung asynchroner Funktionen kontrastieren.
Einführung in die asynchrone Funktion
Eliminierung asynchroner Funktionen (Hinweis: Nur innerhalb einer
async
Funktion gültig )Das grundlegende Problem hierbei ist, dass eine asynchrone Funktion auch eine synchrone Funktion ist, die ein Versprechungsobjekt erzeugt .
Hier ist ein Beispiel für den synchronen Aufruf einer asynchronen Funktion in der node.js-Replikation.
Sie können hypothetisch eine Sprache haben, auch eine dynamisch typisierte, bei der der Unterschied zwischen asynchronen und synchronen Funktionsaufrufen auf der Aufrufsite nicht sichtbar und möglicherweise auf der Definitionssite nicht sichtbar ist.
Wenn Sie eine solche Sprache auf Javascript reduzieren, müssen Sie lediglich alle Funktionen asynchronisieren.
quelle
Mit den Goroutinen der Sprache Go und der Laufzeit der Sprache Go können Sie den gesamten Code so schreiben, als wäre er synchronisiert. Wenn eine Operation in einer Goroutine blockiert wird, wird die Ausführung in anderen Goroutinen fortgesetzt. Und mit Kanälen können Sie problemlos zwischen Goroutinen kommunizieren. Dies ist oft einfacher als Rückrufe in Javascript oder async / await in anderen Sprachen. Unter https://tour.golang.org/concurrency/1 finden Sie einige Beispiele und eine Erklärung.
Außerdem habe ich keine persönlichen Erfahrungen damit, aber ich habe gehört, dass Erlang ähnliche Einrichtungen hat.
Also, ja, es gibt Programmiersprachen wie Go und Erlang, die das synchron / asynchrone Problem lösen, aber leider sind sie noch nicht sehr beliebt. Da diese Sprachen immer beliebter werden, werden die von ihnen bereitgestellten Funktionen möglicherweise auch in anderen Sprachen implementiert.
quelle
go ...
, so sieht es ähnlich aus wieawait ...
nein?go
. Und so gut wie jeder Aufruf, der blockiert werden könnte, wird von der Laufzeit asynchron ausgeführt, die in der Zwischenzeit nur auf eine andere Goroutine umschaltet (kooperatives Multitasking). Sie warten auf eine Nachricht.await
Lesen von einem Kanal<- ch
.Es gibt einen sehr wichtigen Aspekt, der noch nicht angesprochen wurde: Wiedereintritt. Wenn Sie einen anderen Code (dh eine Ereignisschleife) haben, der während des asynchronen Aufrufs ausgeführt wird (und wenn Sie dies nicht tun, warum benötigen Sie dann überhaupt asynchronen Code?), Kann sich der Code auf den Programmstatus auswirken. Sie können die asynchronen Aufrufe nicht vor dem Aufrufer verbergen, da der Aufrufer möglicherweise davon abhängt, dass Teile des Programmstatus für die Dauer seines Funktionsaufrufs unberührt bleiben. Beispiel:
Wenn
bar()
es sich um eine asynchrone Funktion handelt, kann sich diese möglicherweiseobj.x
während der Ausführung ändern. Dies wäre ziemlich unerwartet, ohne den Hinweis, dass der Balken asynchron ist und dieser Effekt möglich ist. Die einzige Alternative wäre, zu vermuten, dass jede mögliche Funktion / Methode asynchron ist, und einen nicht lokalen Status nach jedem Funktionsaufruf erneut abzurufen und zu überprüfen. Dies ist anfällig für subtile Fehler und möglicherweise überhaupt nicht möglich, wenn ein Teil des nicht lokalen Status über Funktionen abgerufen wird. Aus diesem Grund muss der Programmierer wissen, welche der Funktionen das Potenzial haben, den Programmstatus auf unerwartete Weise zu ändern:Jetzt ist klar ersichtlich, dass es sich bei der
bar()
Funktion um eine asynchrone Funktion handelt. Die richtige Vorgehensweise besteht darin, den erwarteten Wert vonobj.x
anschließend erneut zu überprüfen und etwaige Änderungen zu berücksichtigen.Wie bereits in anderen Antworten erwähnt, können sich reine Funktionssprachen wie Haskell diesem Effekt vollständig entziehen, indem sie die Notwendigkeit eines gemeinsamen / globalen Zustands überhaupt vermeiden. Ich habe nicht viel Erfahrung mit funktionalen Sprachen, daher bin ich wahrscheinlich voreingenommen, aber ich denke nicht, dass das Fehlen des globalen Zustands ein Vorteil ist, wenn ich größere Anwendungen schreibe.
quelle
Im Fall von Javascript, das Sie in Ihrer Frage verwendet haben, ist Folgendes zu beachten: Javascript ist ein Singlethread und die Ausführungsreihenfolge ist garantiert, solange keine asynchronen Aufrufe vorliegen.
Wenn Sie also eine Sequenz wie Ihre haben:
Sie werden garantiert, dass in der Zwischenzeit nichts anderes ausgeführt wird. Keine Notwendigkeit für Schlösser oder ähnliches.
Wenn
getNbOfUsers
jedoch asynchron ist, gilt Folgendes:bedeutet, dass während
getNbOfUsers
der Ausführung möglicherweise Ausführungsergebnisse und anderer Code dazwischen ausgeführt werden. Dies kann wiederum eine Sperrung erfordern, je nachdem, was Sie tun.Es ist daher eine gute Idee, sich darüber im Klaren zu sein, wann ein Anruf asynchron ist und wann nicht, da Sie in bestimmten Situationen zusätzliche Vorsichtsmaßnahmen treffen müssen, die Sie bei einem synchronen Anruf nicht treffen müssten.
quelle
getNbOfUsers()
ein Versprechen zurückgibt. Aber genau das ist der Punkt meiner Frage, warum müssen wir es explizit als asynchron schreiben, der Compiler könnte es erkennen und es automatisch auf eine andere Art und Weise handhaben.Dies ist in C ++ wie
std::async
seit C ++ 11 verfügbar .Und mit C ++ 20 können Coroutinen verwendet werden:
quelle
await
(oderco_await
in diesem Fall) Schlüsselwort benötigen ?