Welche Farbe hat deine Funktion?

Ich weiß nichts über dich, aber für mich gibt es keinen besseren Start in den Tag, als mich um das Programmieren zu kümmern. Das Blut kocht beim Anblick einer erfolgreichen Kritik an einer der "kühnen" Sprachen, die von den Plebejern verwendet wird und die den ganzen Arbeitstag zwischen schüchternen Besuchen bei StackOverflow damit gequält wird.


(In der Zwischenzeit verwenden Sie und ich nur die aufgeklärteste Sprache und die ausgefeiltesten Werkzeuge, die für die geschickten Hände von Meistern wie uns entwickelt wurden.)


Natürlich gehe ich als Autor der Predigt Risiken ein. Vielleicht gefällt dir die Sprache, über die ich mich lustig mache! Eine rücksichtslose Broschüre könnte versehentlich eine wütende Menge von Handys mit Heugabeln und Fackeln in meinem Blog erscheinen lassen.


Um mich vor dem gerechten Feuer zu schützen und Ihre (wahrscheinlich heiklen) Gefühle nicht zu beleidigen, werde ich über die Sprache sprechen ...


... der gerade aufgetaucht ist. Über ein Strohbildnis, dessen einzige Aufgabe es ist, Kritiker auf dem Scheiterhaufen zu verbrennen.


Ich weiß, das klingt albern, aber glauben Sie mir, am Ende werden wir sehen, wessen Gesicht (oder Gesichter) auf einen Strohkopf gemalt wurden.


Neue Sprache


Es wird ein Overkill sein, eine völlig neue (und blöde) Sprache nur für einen Blog-Artikel zu lernen. Nehmen wir also an, dass sie der Sprache, die wir bereits kennen, sehr ähnlich ist. Zum Beispiel Javascript. Geschweifte Klammern und Semikolons. if , while usw. - Lingua Franca unserer Menge.


Ich habe JS nicht gewählt, weil dieser Artikel über ihn ist. Es ist nur eine Sprache, in die der durchschnittliche Leser wahrscheinlich geraten wird. Voila:


 function thisIsAFunction(){ return "!"; } 

Da unser Stofftier eine coole (schlecht gelesene) Sprache ist, hat es erstklassige Funktionen . Sie können also so etwas schreiben:


 //  ,     , //    function filter(collection, predicate) { var result = []; for (var i = 0; i < collection.length; i++) { if (predicate(collection[i])){ result.push(collection[i]); } } return result; } 

Dies ist eine der erstklassigen Funktionen, und wie der Name schon sagt, sind sie cool und äußerst nützlich. Sie sind es wahrscheinlich gewohnt, Datensammlungen mit ihrer Hilfe zu transformieren, aber sobald Sie das Konzept verstanden haben, verwenden Sie sie überall, verdammt noch mal.


Vielleicht in Tests:


 describe("", function(){ it(" ", function(){ expect("").not.toBe(""); }); }; 

Oder wenn Sie die Daten analysieren müssen:


 tokens.match(Token.LEFT_BRACKET, function(token){ // Parse a list literal... tokens.consume(Token.RIGHT_BRACKET); }); 

Nach dem Beschleunigen schreiben Sie alle Arten von coolen wiederverwendbaren Bibliotheken und Anwendungen, die sich um Funktionen, Funktionsaufrufe und Funktionsrückgaben von Funktionen drehen - eine Funktionskabine.


Übersetzer: im Original "Functapalooza". Das Präfix -a-palooza ist so cool, dass Sie es mit allen teilen möchten.

Welche Farbe hat deine Funktion?


Und hier beginnen die Kuriositäten. Unsere Sprache hat eine Besonderheit:


1. Jede Funktion hat eine Farbe.


Jede Funktion - ein anonymer Rückruf oder eine reguläre Funktion mit einem Namen - ist entweder rot oder blau. Da die Hervorhebung des Codes in unserem Blog nicht die unterschiedliche Farbe der Funktionen hervorhebt, stimmen wir zu, dass die Syntax wie folgt lautet:


 blue*function doSomethingAzure(){ //   ... } red*function doSomethingCarnelian(){ //    ... } 

Unsere Sprache hat keine farblosen Funktionen. Möchten Sie eine Funktion erstellen? - muss eine Farbe wählen. Das sind die Regeln. Und es gibt noch ein paar Regeln, denen Sie folgen müssen:


2. Farbe beeinflusst die Art und Weise, wie die Funktion aufgerufen wird


Stellen Sie sich vor, es gibt zwei Syntaxen zum Aufrufen von Funktionen - "blau" und "rot". So etwas wie:


 doSomethingAzure(...)*blue; doSomethingCarnelian()*red; 

Wenn Sie eine Funktion aufrufen, müssen Sie einen Aufruf verwenden, der ihrer Farbe entspricht. Wenn Sie es nicht erraten haben - sie haben die rote Funktion mit *blue nach den Klammern aufgerufen (oder umgekehrt) - wird etwas sehr Schlimmes passieren. Ein längst vergessener Albtraum in der Kindheit, wie ein Clown mit Schlangen anstelle von Händen, die sich unter Ihrem Bett versteckten. Er wird aus dem Monitor springen und deine Augen lutschen.


Dumme Regel, oder? Oh, aber noch etwas:


3. Nur die rote Funktion kann die rote Funktion verursachen.


Sie können die blaue Funktion von rot aufrufen. Das ist koscher:


 red*function doSomethingCarnelian(){ doSomethingAzure()*blue; } 

Aber nicht umgekehrt. Wenn Sie versuchen:


 blue*function doSomethingAzure(){ doSomethingCarnelian()*red; } 

- Sie werden vom alten Clown Spider Maw besucht.


Dies macht es schwieriger, höhere Funktionen wie filter() aus dem Beispiel zu schreiben. Wir müssen für jede neue Funktion eine Farbe auswählen. Dies wirkt sich auf die Farbe der Funktionen aus, die wir an sie übergeben können. Die naheliegende Lösung besteht darin, filter() rot zu machen. Dann können wir mindestens rote, mindestens blaue Funktionen aufrufen. Aber dann werden wir wegen des nächsten Dorns in der Dornenkrone verletzt, der die gegebene Sprache ist:


4. Rote Funktionen verursachen Schmerzen


Wir werden diesen „Schmerz“ nicht genau bestimmen. Stellen Sie sich vor, der Programmierer muss jedes Mal durch den Rahmen springen, wenn er die rote Funktion aufruft. Der Aufruf ist möglicherweise zu mehrsilbig oder Sie können die Funktion in einigen Ausdrücken nicht ausführen. Oder Sie können nur über ungerade Zeilen auf die rote Funktion zugreifen.


Es spielt keine Rolle, was es ist, aber wenn Sie sich entscheiden, die Funktion rot zu machen, möchte jeder, der Ihre API verwendet, in Ihren Kaffee spucken oder etwas Schlimmeres tun.


Die offensichtliche Lösung in diesem Fall besteht darin, niemals rote Funktionen zu verwenden. Machen Sie einfach alles blau, und Sie befinden sich wieder in der normalen Welt, in der alle Funktionen dieselbe Farbe haben. Dies entspricht der Tatsache, dass sie keine Farbe haben und unsere Sprache nicht völlig dumm ist.


Leider die Sadisten, die diese Sprache entwickelt haben (jeder weiß, dass die Autoren von Programmiersprachen Sadisten sind, oder?) Stecken Sie uns den letzten Dorn im Auge:


5. Einige der Kernfunktionen der Sprache sind rot.


Einige in die Plattform integrierte Funktionen, Funktionen, die wir verwenden müssen und die nicht von uns selbst geschrieben werden können, sind nur in rot verfügbar. An diesem Punkt kann eine intelligente Person anfangen zu vermuten, dass diese Sprache uns hasst.


Dies ist alles die Schuld der funktionalen Sprachen!


Sie könnten denken, dass das Problem darin besteht, dass wir versuchen, Funktionen höherer Ordnung zu verwenden. Wenn wir einfach aufhören, mit all diesem funktionalen Unsinn herumzuspielen, und anfangen, normale blaue Funktionen erster Ordnung (Funktionen, die nicht mit anderen Funktionen funktionieren - Anmerkung des Übersetzers) zu schreiben, wie von Gott geplant, werden wir all diesen Schmerz los.


Wenn wir nur blaue Funktionen aufrufen, machen wir alle unsere Funktionen blau. Ansonsten machen wir rot. Bis wir Funktionen erstellen, die Funktionen akzeptieren, müssen wir uns keine Gedanken über "Polymorphismus zur Farbe der Funktion" (polychromatisch?) Oder anderen Unsinn machen.


Aber leider sind Funktionen höherer Ordnung nur ein Beispiel. Das Problem tritt jedes Mal auf, wenn wir unser Programm in Funktionen zur Wiederverwendung aufteilen möchten.


Zum Beispiel haben wir einen netten kleinen Code, der, ich weiß nicht, den Dijkstra-Algorithmus über ein Diagramm implementiert, das darstellt, wie sehr Ihre sozialen Verbindungen sich gegenseitig unter Druck setzen. (Ich habe viel Zeit damit verbracht, zu entscheiden, was das Ergebnis bedeuten würde. Transitive Unerwünschtheit?)


Später mussten Sie diesen Algorithmus woanders verwenden. Natürlich verpacken Sie den Code in eine separate Funktion. Rufen Sie sie vom alten und vom neuen Ort an. Aber welche Farbe soll die Funktion haben? Sie werden wahrscheinlich versuchen, es blau zu machen, aber was ist, wenn es eine dieser fiesen "nur roten" Funktionen aus der Kernel-Bibliothek verwendet?


Angenommen, der neue Ort, von dem aus Sie die Funktion aufrufen möchten, ist blau. Jetzt müssen Sie den aufrufenden Code in Rot umschreiben. Und wiederholen Sie dann die Funktion, die diesen Code aufruft. Puh Sie müssen sich sowieso ständig an die Farbe erinnern. Dies wird der Sand in Ihrer Badehose bei einem Strandurlaubsprogramm sein.


Farb-Allegorie


Tatsächlich spreche ich nicht über Farbe. Dies ist eine Allegorie, ein literarisches Mittel. Fuck - hier geht es nicht um die Sterne auf den Bäuchen , hier geht es um das Rennen. Sie vermuten wahrscheinlich schon ...


Rote Funktionen - Asynchron


Wenn Sie in JavaScript oder Node.js programmieren, schreiben Sie jedes Mal, wenn Sie eine Funktion definieren, die eine Rückruffunktion (Rückruf) aufruft, um das Ergebnis zurückzugeben, eine rote Funktion. Schauen Sie sich diese Liste von Regeln an und beachten Sie, wie sie in meine Metapher passen:


  1. Synchrone Funktionen geben ein Ergebnis zurück, asynchrone Funktionen nicht. Im Gegenzug rufen sie einen Rückruf auf.
  2. Synchrone Funktionen geben das Ergebnis als Rückgabewert zurück, asynchrone Funktionen geben es zurück und verursachen den Rückruf, den Sie an sie übergeben haben.
  3. Sie können eine asynchrone Funktion nicht von einer synchronen aus aufrufen, da Sie das Ergebnis erst kennen, wenn die asynchrone Funktion später ausgeführt wird.
  4. Asynchrone Funktionen werden aufgrund von Rückrufen nicht zu Ausdrücken kompiliert, erfordern eine unterschiedliche Behandlung ihrer Fehler und können nicht in einem try/catch Block oder in einer Reihe anderer Ausdrücke verwendet werden, die das Programm steuern.
  5. Das Ganze an Node.js ist, dass die Kernel-Bibliothek vollständig asynchron ist. (Obwohl sie zurück spenden und damit beginnen, _Sync() Versionen zu vielen Dingen hinzuzufügen.)

Wenn Leute über "Rückrufhölle" sprechen, sprechen sie darüber, wie ärgerlich es ist, "rote" Funktionen in ihrer Sprache zu haben. Wenn sie 4089 Bibliotheken für die asynchrone Programmierung erstellen (bereits 2019 11217 - ca. Übersetzer), versuchen sie, das Problem auf Bibliotheksebene zu bewältigen, dass sie mit der Sprache nicht weiterkommen.


Ich verspreche, dass die Zukunft besser ist


in Übersetzung: "Ich verspreche, dass die Zukunft besser ist" Das Wortspiel aus Titel und Inhalt des Abschnitts geht verloren

Die Leute bei Node.js haben lange erkannt, dass Rückrufe weh tun, und suchten nach Lösungen. Eine der Techniken, die viele Menschen inspiriert hat, sind promises , die Sie vielleicht auch unter dem Spitznamen futures .


Anstatt "Versprechen" als "Versprechen" zu übersetzen, wurde in der russischen IT das Rückverfolgungspapier aus dem Englischen - "Versprechen" - eingerichtet. Das Wort "Futures" wird so verwendet, wie es ist, wahrscheinlich weil die "Futures" bereits von Finanzsprache besetzt sind.

Promis ist ein Wrapper für Callback und Fehlerbehandlungsroutine. Wenn Sie daran denken, einen Rückruf für das Ergebnis und einen weiteren Rückruf für den Fehler zu übergeben, ist die future die Verkörperung dieser Idee. Dies ist ein Basisobjekt, das eine asynchrone Operation ist.


Ich habe gerade ein paar ausgefallene Formulierungen bekommen und es mag nach einer großartigen Lösung klingen, aber meistens ist es Schlangenöl . Versprechen erleichtern das Schreiben von asynchronem Code ein wenig. Sie lassen sich leichter zu Ausdrücken zusammensetzen, daher ist Regel 4 etwas weniger streng.


Aber um ehrlich zu sein, ist es wie der Unterschied zwischen einem Schlag in den Bauch oder in der Leiste. Ja, es tut nicht so weh, aber niemand wird sich über eine solche Wahl freuen.


Sie können immer noch keine Versprechen für die Ausnahmebehandlung oder andere verwenden
Betreiber verwalten. Sie können keine Funktion aufrufen, die aus synchronem Code future zurückgibt. (Sie können , aber dann erfindet der nächste Betreuer Ihres Codes eine Zeitmaschine, kehrt in dem Moment zurück, in dem Sie es getan haben, und steckt Ihnen aus Grund 2 einen Bleistift ins Gesicht.)


Versprechen teilen deine Welt immer noch in asynchrone und synchrone Hälften mit all dem daraus resultierenden Leiden. Selbst wenn Ihre Sprache promises oder futures , sieht sie meiner ausgestopften sehr ähnlich.


(Ja, dazu gehört auch der Dart , den ich benutze. Deshalb bin ich so froh, dass ein Teil des Teams andere Ansätze zur Parallelität ausprobiert. )


Projektlink offiziell aufgegeben

Ich warte auf eine Lösung


C # -Programmierer fühlen sich wahrscheinlich selbstgefällig (der Grund, warum sie immer mehr Opfer werden, ist, dass Halesberg und das Unternehmen alles und die Sprache mit syntaktischem Zucker bestreuen). In C # können Sie das Schlüsselwort await , um eine asynchrone Funktion aufzurufen.


Dies macht asynchrone Anrufe so einfach wie synchron, indem ein niedliches kleines Schlüsselwort hinzugefügt wird. Sie können einen await Aufruf in Ausdrücke einfügen, sie bei der Ausnahmebehandlung im Anweisungsfluss verwenden. Du kannst verrückt werden. Lassen Sie den Regen wie Geld für Ihr neues Rapper-Album warten.


Async-await ist nett, also fügen wir es Dart hinzu. Es ist viel einfacher , asynchronen Code damit zu schreiben . Aber wie immer gibt es ein "Aber". Hier ist es. Aber ... Sie teilen die Welt immer noch in zwei Hälften. Asynchrone Funktionen sind jetzt einfacher zu schreiben, aber sie sind immer noch asynchrone Funktionen.


Sie haben noch zwei Farben. Async-await löst das nervige Problem Nr. 4 - sie machen das Aufrufen von roten Funktionen nicht schwieriger als das Aufrufen von blauen. Aber der Rest der Regeln ist immer noch hier:


  1. Synchrone Funktionen geben Werte zurück, asynchrone Funktionen geben einen Wrapper ( Task<T> in C # oder Future<T> in Dart) um den Wert zurück.
  2. Synchron gerade aufgerufen, asynchrone Notwendigkeit await .
  3. Durch Aufrufen einer asynchronen Funktion erhalten Sie ein Wrapper-Objekt, wenn Sie wirklich einen Wert möchten. Sie können den Wert erst erweitern, wenn Sie Ihre Funktion asynchron gemacht und mit await aufgerufen haben (siehe jedoch den nächsten Absatz).
  4. Neben ein wenig wartender Dekoration haben wir zumindest dieses Problem gelöst.
  5. Die C # -Kernbibliothek ist älter als Asynchronität, daher glaube ich, dass sie dieses Problem nie hatten.

Async wirklich besser. Ich würde es vorziehen, an jedem Tag der Woche asynchron auf nackte Rückrufe zu warten. Aber wir lügen uns selbst an, wenn wir denken, dass alle Probleme gelöst sind. Sobald Sie anfangen, Funktionen höherer Ordnung zu schreiben oder Code wiederzuverwenden, stellen Sie erneut fest, dass die Farbe noch vorhanden ist und durch Ihren gesamten Quellcode blutet.


Welche Sprache ist keine Farbe?


JS, Dart, C # und Python haben dieses Problem. CoffeeScript und die meisten anderen Sprachen werden auch in JS kompiliert (und Dart geerbt). Ich denke sogar ClojureScript hat diesen Haken, trotz ihrer aktiven Bemühungen mit core.async


Möchten Sie wissen, welche nicht? Java Ich Recht? Wie oft sagen Sie: "Ja, Java allein macht es richtig"? Und so geschah es. Zu ihrer Verteidigung versuchen sie aktiv, ihr Versehen zu korrigieren, indem sie futures und asynchrone E / A fördern. Es ist wie ein Rennen, das immer schlimmer ist.


alles ist schon in Java

C # kann dieses Problem auch umgehen. Sie entschieden sich für Farbe. Bevor sie async-await und all diesen Task<T> -Junk hinzugefügt haben, können Sie einfach reguläre synchrone API-Aufrufe verwenden. Drei weitere Sprachen, die kein Farbproblem haben: Go, Lua und Ruby.


Ratet mal, was sie gemeinsam haben?


Streams. Genauer gesagt: viele unabhängige Call-Stacks , die wechseln können . Dies sind nicht unbedingt Threads des Betriebssystems. Coroutinen in Go, Coroutinen in Lua und Threads in Ruby sind alle ausreichend.


(Deshalb gibt es diese kleine Einschränkung für C # - Sie können den asynchronen Schmerz in C # vermeiden, indem Sie Threads verwenden.)


Erinnerung an vergangene Operationen


Das grundlegende Problem besteht darin, "wie am selben Ort fortzufahren, wenn der (asynchrone) Vorgang abgeschlossen ist"? Sie stürzten sich in den Abgrund des Aufrufstapels und riefen dann eine Art E / A-Operation auf. Zur Beschleunigung verwendet dieser Vorgang die zugrunde liegende asynchrone API Ihres Betriebssystems. Sie können es kaum erwarten, bis es abgeschlossen ist. Sie müssen zur Ereignisschleife Ihrer Sprache zurückkehren und dem Betriebssystem Zeit geben, um den Vorgang abzuschließen.


Sobald dies passiert ist, müssen Sie wieder aufnehmen, was Sie getan haben. Normalerweise merkt sich die Sprache über den Aufrufstapel, wo sie war. Er geht alle Funktionen durch, die im Moment aufgerufen wurden, und schaut, wo der Befehlszähler in jeder von ihnen zeigt.


Um jedoch asynchrone E / A auszuführen, müssen Sie den gesamten Aufrufstapel in C abwickeln und verwerfen. Geben Sie Trick-22 ein. Sie haben superschnelle E / A, aber Sie können das Ergebnis nicht verwenden! Alle Sprachen mit asynchroner E / A unter der Haube - oder im Fall von JS die Browser-Ereignisschleife - sind gezwungen, dies irgendwie zu handhaben.


Mit seinen für immer marschierenden Rückrufen stopft Node all diese Anrufe in Schließungen. Wenn Sie schreiben:


 function makeSundae(callback) { scoopIceCream(function (iceCream) { warmUpCaramel(function (caramel) { callback(pourOnIceCream(iceCream, caramel)); }); }); } 

Jeder dieser funktionalen Ausdrücke schließt seinen gesamten umgebenden Kontext. Dadurch werden Parameter wie iceCream und caramel vom Aufrufstapel auf den Heap übertragen . Wenn eine externe Funktion ein Ergebnis zurückgibt und der Aufrufstapel zerstört wird, ist das cool. Die Daten befinden sich noch irgendwo auf dem Haufen.


Das Problem ist, dass Sie jeden dieser verdammten Anrufe erneut beleben müssen. Es gibt sogar einen speziellen Namen für diese Konvertierung: Continuation-Passing-Stil


verbinde wilde Funktionalität

Dies wurde in den 70er Jahren von Sprachhackern als Zwischendarstellung für den Einsatz unter der Haube von Compilern erfunden. Dies ist eine sehr bizarre Art, Code einzuführen, der es einfacher macht, einige Compiler-Optimierungen durchzuführen.


Niemand hätte jemals gedacht, dass ein Programmierer solchen Code schreiben könnte. Und dann erschien Node und plötzlich geben wir alle vor, ein Compiler-Backend zu schreiben. Wo haben wir den falschen Weg eingeschlagen?


Beachten Sie, dass Versprechen und futures nicht wirklich viel helfen. Wenn Sie sie verwenden, wissen Sie, dass Sie immer noch riesige Schichten funktionaler Ausdrücke aufstapeln . Sie übergeben sie einfach an .then() anstelle der asynchronen Funktion selbst.


Warten auf eine generierte Lösung


Async-Warten hilft wirklich . Wenn Sie unter der Haube auf den Compiler schauen, wenn er auf ihn await , werden Sie await , dass er tatsächlich die CPS-Konvertierung durchführt. Aus diesem Grund müssen Sie await in C # verwenden - dies ist ein Hinweis für den Compiler - "Stoppen Sie die Funktion hier in der Mitte." Alles nach dem await wird zu einer neuen Funktion, die der Compiler in Ihrem Namen synthetisiert.


Aus diesem Grund benötigt async-await keine Laufzeitunterstützung innerhalb des .NET Frameworks. Der Compiler kompiliert dies zu einer Kette verwandter Abschlüsse, mit denen er bereits umgehen kann. (Interessanterweise erfordern Closures auch keine Laufzeitunterstützung. Sie werden in anonyme Klassen kompiliert. In C # sind Closures nur Objekte.)


Sie fragen sich wahrscheinlich, wann ich die Generatoren erwähne. Gibt es in Ihrer Sprache yield ? Dann kann er etwas sehr Ähnliches tun.


(Ich glaube, dass Generatoren und asynchrones Warten tatsächlich isomorph sind. Irgendwo in den staubigen Ecken und Winkeln meiner Festplatte befindet sich ein Code, der eine Spielschleife für Generatoren implementiert, die nur asynchrones Warten verwenden.)


Also wo bin ich? Oh ja. Mit Rückrufen, Versprechungen, asynchronem Warten und Generatoren übernehmen Sie am Ende Ihre asynchrone Funktion und teilen sie in eine Reihe von Abschlüssen auf, die auf dem Haufen leben.


Ihre Funktion ruft zur Laufzeit extern auf. Wenn die Ereignisschleife oder die E / A-Operation beendet ist, wird Ihre Funktion aufgerufen und an der Stelle fortgesetzt, an der sie sich befand. Dies bedeutet jedoch, dass alles, was über Ihre Funktion hinausgeht, auch zurückkehren sollte. Sie müssen noch den gesamten Stapel wiederherstellen.


Daher kommt die Regel "Es ist möglich, die rote Funktion nur von der roten Funktion aus aufzurufen". Sie müssen den gesamten Aufrufstapel in Closures für main() oder den Event-Handler speichern.


Stack-Implementierung aufrufen


Bei Verwendung von Threads ( grün oder Betriebssystem) müssen Sie dies jedoch nicht tun. Sie können einfach den gesamten Thread anhalten und zum Betriebssystem oder zur Ereignisschleife springen, ohne von all diesen Funktionen zurückkehren zu müssen .


Die Go-Sprache macht dies meines Wissens am perfektesten. Sobald Sie eine E / A-Operation ausführen, parkt Go diese Coroutine und setzt alle anderen fort, die nicht durch E / A blockiert sind.


Wenn Sie sich die E / A-Operationen in der Golang-Standardbibliothek ansehen, scheinen sie synchron zu sein. Mit anderen Worten, sie funktionieren einfach und geben das Ergebnis zurück, wenn sie fertig sind. Diese Synchronisation bedeutet jedoch nicht dasselbe wie in Javascript. Go- , IO . Go .


Go — , . , , .


, API, , . .





, , . , . , 50% .


, , , .


Javascript -, , , JS , JS , . , JS .


, ( ) — , , , async . import threading ( , AsyncIO, Twisted Tornado, ).


, , , , , , .


, Go, Go .


, , , ( - ) , "async-await ". .


, .


, , .

Source: https://habr.com/ru/post/de466337/


All Articles