Quantenmechanik von Berechnungen in JS

Hallo, mein Name ist Dmitry Karlovsky und ich ... arbeitslos. Daher habe ich viel Freizeit, um Musik, Sport, Kreativität, Sprachen, JS-Konferenzen und Informatik zu spielen. Ich erzähle Ihnen von den neuesten Forschungsergebnissen auf dem Gebiet der halbautomatischen Aufteilung langer Berechnungen in kleine Quanten von mehreren Millisekunden, die zu einer Miniaturbibliothek $mol_fiber . Aber lassen Sie uns zuerst die Probleme skizzieren, die wir lösen werden.


Quanta!


Dies ist eine Textversion der gleichnamigen Aufführung bei HolyJS 2018 Piter . Sie können es entweder als Artikel lesen oder in der Präsentationsoberfläche öffnen oder ein Video ansehen .


Problem: Geringe Reaktionsfähigkeit


Wenn wir stabile 60 Bilder pro Sekunde haben möchten, haben wir nur 16 mit einer Kleinigkeit von Millisekunden, um die ganze Arbeit zu erledigen, einschließlich dessen, was der Browser tut, um die Ergebnisse auf dem Bildschirm anzuzeigen.


Aber was ist, wenn wir länger fließen? Dann wird der Benutzer eine verzögerte Oberfläche beobachten, die die Animation und dergleichen der UX-Verschlechterung verhindert.


Geringe reaktionsfähigkeit


Problem: Kein Entkommen


Es kommt vor, dass das Ergebnis für uns nicht mehr interessant ist, während wir die Berechnungen durchführen. Zum Beispiel haben wir eine virtuelle Schriftrolle, die der Benutzer aktiv zieht, aber wir können nicht mithalten und den tatsächlichen Bereich nicht rendern, bis das vorherige Rendering die Kontrolle über die Verarbeitung von Benutzerereignissen zurückgibt.


Kann nicht rückgängig gemacht werden


Unabhängig davon, wie lange wir arbeiten, sollten wir im Idealfall weiterhin Ereignisse verarbeiten und jederzeit in der Lage sein, die begonnenen, aber noch nicht abgeschlossenen Arbeiten abzubrechen.


Ich bin schnell und ich weiß es


Aber was ist, wenn unsere Arbeit nicht eine, sondern mehrere, sondern ein Strom ist? Stellen Sie sich vor, Sie fahren mit Ihrem frisch gekauften gelben Lotus und fahren bis zum Bahnübergang. Wenn es kostenlos ist, können Sie es in Sekundenbruchteilen verschieben. Aber ..


Kühlt automatisch ab


Problem: Keine Parallelität


Wenn die Kreuzung von einem Kilometerzug besetzt ist, müssen Sie stehen und zehn Minuten warten, bis sie vorbeifährt. Nicht dafür hast du einen Sportwagen gekauft, oder?


Schnell warten langsam


Und wie cool wäre es, wenn dieser Zug in 10 Züge zu je 100 Metern aufgeteilt würde und zwischen ihnen mehrere Minuten Zeit wären, um durchzukommen! Sie würden dann nicht so spät sein.


Was sind nun die Lösungen für diese Probleme in der JS-Welt?


Lösung: Arbeiter


Das erste, was mir in den Sinn kommt: Lassen Sie uns einfach alle komplexen Berechnungen in einem separaten Thread zusammenfassen. Dazu haben wir einen Mechanismus für WebWorker.


Arbeiterlogik


Ereignisse aus dem UI-Stream werden an den Worker übergeben. Dort werden sie verarbeitet und Anweisungen, was und wie auf der Seite geändert werden soll, werden bereits zurückgegeben. Auf diese Weise speichern wir den UI-Stream vor einer großen Rechenschicht, aber nicht alle Probleme werden auf diese Weise gelöst, und zusätzlich werden neue hinzugefügt.


Arbeiter: Probleme: (De) Serialisierung


Die Kommunikation zwischen Streams erfolgt durch Senden von Nachrichten, die in einen Byte-Stream serialisiert, in einen anderen Stream übertragen und dort in Objekte analysiert werden. All dies ist viel langsamer als ein direkter Methodenaufruf innerhalb eines einzelnen Threads.


(De) Serialisierung


Arbeiter: Probleme: Nur asynchron


Nachrichten werden streng asynchron übertragen. Und dies bedeutet, dass einige Funktionen, die ich Sie bitte, nicht verfügbar sind. Beispielsweise können Sie den Aufstieg eines UI-Ereignisses von einem Worker nicht stoppen, da das Ereignis im UI-Thread zum Zeitpunkt des Starts des Handlers bereits seinen Lebenszyklus abschließt.


Nachrichtenwarteschlangen


Arbeiter: Probleme: Begrenzte APIs


Die folgenden APIs stehen uns in den Workern nicht zur Verfügung.


  • DOM, CSSOM
  • Leinwand
  • GeoLocation
  • Geschichte & Ort
  • HTTP-Anforderungen synchronisieren
  • XMLHttpRequest.responseXML
  • Fenster

Arbeiter: Probleme: Kann nicht storniert werden


Und wieder haben wir keine Möglichkeit, die Berechnungen im Woker zu stoppen.


Hör auf!


Ja, wir können den gesamten Arbeiter stoppen, aber das wird alle darin enthaltenen Aufgaben stoppen.
Ja, Sie können jede Aufgabe in einem separaten Worker ausführen, dies ist jedoch sehr ressourcenintensiv.


Lösung: Faser reagieren


Sicherlich haben viele gehört, dass FaceBook React heldenhaft umschreibt und alle darin enthaltenen Berechnungen in eine Reihe kleiner Funktionen aufteilt, die von einem speziellen Scheduler gestartet werden.


Tricky React Fiber Logic


Ich werde nicht auf Details seiner Implementierung eingehen, da dies ein separates großes Thema ist. Ich werde nur einige Funktionen erwähnen, aufgrund derer es möglicherweise nicht zu Ihnen passt.


Faser reagieren: Reaktion erforderlich


Wenn Sie Angular, Vue oder ein anderes Framework als React verwenden, ist React Fibre natürlich für Sie nutzlos.


Reagieren Sie genießen!


React Fibre: Nur Rendern


Reagieren - deckt nur die Rendering-Ebene ab. Alle anderen Schichten der Anwendung bleiben ohne Quantisierung.


Nicht so schnell!


React Fibre speichert Sie nicht, wenn Sie beispielsweise einen großen Datenblock nach schwierigen Bedingungen filtern müssen.


React Fibre: Quantisierung ist deaktiviert


Trotz der behaupteten Unterstützung für die Quantisierung ist sie standardmäßig immer noch deaktiviert, da sie die Abwärtskompatibilität beeinträchtigt.


Marketingfalle


Die Quantisierung in React ist immer noch eine experimentelle Sache. Seid vorsichtig!


React Fibre: Debug ist Schmerz


Wenn Sie die Quantisierung aktivieren, stimmt Callstack nicht mehr mit Ihrem Code überein, was das Debuggen erheblich erschwert. Aber wir werden auf dieses Thema zurückkommen.


Der ganze Schmerz des Debuggens


Lösung: Quantisierung


Versuchen wir, den React Fibre-Ansatz zu verallgemeinern, um die genannten Nachteile zu beseitigen. Wir möchten im Rahmen eines Streams bleiben, aber lange Berechnungen in kleine Quanten aufteilen, zwischen denen der Browser die bereits an der Seite vorgenommenen Änderungen rendern kann, und auf Ereignisse reagieren.


Flammendiagramme


Oben sehen Sie eine lange Berechnung, die die ganze Welt um mehr als 100 ms gestoppt hat. Und von unten - die gleiche Berechnung, jedoch in Zeitscheiben von etwa 16 ms unterteilt, was durchschnittlich 60 Bilder pro Sekunde ergab. Da wir normalerweise nicht wissen, wie viel Zeit die Berechnungen in Anspruch nehmen werden, können wir sie nicht manuell im Voraus in 16 ms aufteilen. Daher benötigen wir eine Art Laufzeitmechanismus, der die Zeit misst, die zum Abschließen der Aufgabe benötigt wird, und wenn das Quantum überschritten wird, wodurch die Ausführung bis zum nächsten Animationsframe angehalten wird. Lassen Sie uns darüber nachdenken, welche Mechanismen wir haben, um solche angehaltenen Aufgaben hier zu implementieren.


Parallelität: Fasern - stapelbare Coroutinen


In Sprachen wie Go und D gibt es eine Redewendung wie "Coroutine mit Stapel", es ist auch eine "Faser" oder "Faser".


 import { Future } from 'node-fibers' const one = ()=> Future.wait( future => setTimeout( future.return ) ) const two = ()=> one() + 1 const three = ()=> two() + 1 const four = ()=> three() + 1 Future.task( four ).detach() 

Im Codebeispiel sehen Sie die one Funktion, die die aktuelle Glasfaser anhalten kann, aber selbst eine vollständig synchrone Schnittstelle hat. Die two , three und four Funktionen sind reguläre synchrone Funktionen, die nichts über Glasfaser wissen. In ihnen können Sie alle Funktionen von Javascript vollständig nutzen. Und schließlich führen wir in der letzten Zeile einfach die four Funktionen in einer separaten Faser aus.


Die Verwendung von Fasern ist recht praktisch, aber um sie zu unterstützen, benötigen Sie Laufzeitunterstützung, die die meisten JS-Interpreter nicht haben. Für NodeJS gibt es jedoch eine native node-fibers Erweiterung, die diese Unterstützung hinzufügt. Leider sind in keinem Browser Browser verfügbar.


Parallelität: FSM - stapellose Coroutinen


In Sprachen wie C # und jetzt JS werden "stapellose Coroutinen" oder "asynchrone Funktionen" unterstützt. Solche Funktionen sind eine Zustandsmaschine unter der Haube und wissen nichts über den Stapel. Daher müssen sie mit dem speziellen Schlüsselwort "async" gekennzeichnet werden, und Orte, an denen sie angehalten werden können, werden "erwartet".


 const one = ()=> new Promise( done => setTimeout( done ) ) const two = async ()=> ( await one() ) + 1 const three = async ()=> ( await two() ) + 1 const four = async ()=> ( await three() ) + 1 four() 

Da wir die Berechnung möglicherweise jederzeit verschieben müssen, müssen fast alle Funktionen in der Anwendung asynchron ausgeführt werden. Dies ist nicht nur die Komplexität des Codes, sondern wirkt sich auch stark auf die Leistung aus. Darüber hinaus unterstützen viele APIs, die Rückrufe akzeptieren, immer noch keine asynchronen Rückrufe. Ein auffälliges Beispiel ist die reduce eines Arrays.


Parallelität: Halbfasern - Neustart


Versuchen wir, etwas Ähnliches wie Glasfaser zu tun, indem wir nur die Funktionen verwenden, die uns in jedem modernen Browser zur Verfügung stehen.


 import { $mol_fiber_async , $mol_fiber_start } from 'mol_fiber/web' const one = ()=> $mol_fiber_async( back => setTimeout( back ) ) const two = ()=> one() + 1 const three = ()=> two() + 1 const four = ()=> three() + 1 $mol_fiber_start( four ) 

Wie Sie sehen können, wissen die Zwischenfunktionen nichts über Unterbrechungen - dies ist reguläres JS. Nur die one Funktion kennt die Möglichkeit der Federung. Um die Berechnung abzubrechen, wirft sie einfach Promise als Ausnahme. In der letzten Zeile führen wir die four Funktionen in einer separaten Pseudofaser aus, die die darin ausgelösten Ausnahmen überwacht. Wenn Promise eintrifft, abonniert es seine resolve und startet die Faser neu.


Zahlen


Um zu zeigen, wie Pseudofasern funktionieren, schreiben wir einen kniffligen Code.


Typisches Ausführungsdiagramm


Stellen wir uns vor, dass die step hier etwas in die Konsole schreibt und 20 ms lang andere harte Arbeit leistet. Die walk Funktion ruft step zweimal auf und protokolliert den gesamten Prozess. In der Mitte wird angezeigt, was jetzt in der Konsole angezeigt wird. Und rechts ist der Zustand des Pseudofaserbaums.


$ mol_fiber: keine Quantisierung


Lassen Sie uns diesen Code ausführen und sehen, was passiert.


Ausführung ohne Quantisierung


Bisher ist alles einfach und offensichtlich. Der Pseudofaserbaum ist natürlich nicht beteiligt. Und alles wäre in Ordnung, aber dieser Code wird länger als 40 ms ausgeführt, was wertlos ist.


$ mol_fiber: zuerst zwischenspeichern


Lassen Sie uns beide Funktionen in einen speziellen Wrapper einwickeln, der sie in einer Pseudofaser ausführt, und sehen, was passiert.


Caches ausführen


Hierbei ist zu beachten, dass für jeden Ort, an dem die one Funktion innerhalb der walk Faser aufgerufen wird, eine separate Faser erstellt wurde. Das Ergebnis des ersten Aufrufs wurde zwischengespeichert, aber anstelle des zweiten wurde Promise geworfen, da wir unsere Zeitscheibe erschöpft hatten.


$ mol_fiber: Cache Sekunde


Im ersten Frame wird das Promise im nächsten automatisch aufgelöst, was zu einem Neustart der walk .


Cache-wiederverwendung


Wie Sie sehen können, geben wir aufgrund des Neustarts erneut "start" und "first done" an die Konsole aus, aber "first begin" ist bereits weg, da es sich in der Glasfaser befindet und der Cache früher gefüllt wurde, weshalb der Handler mehr ist nicht angerufen. Wenn der Cache der walk Faser gefüllt ist, werden alle eingebetteten Fasern zerstört, da die Ausführung sie niemals erreichen wird.


Warum wurde first begin Drucken begonnen und first done first begin Drucken? Es geht nur um Idempotenz. console.log - nicht idempotenter Vorgang, wie oft Sie ihn aufrufen, so oft wird der Konsole ein Eintrag hinzugefügt. Die Faser, die in einer anderen Faser ausgeführt wird, ist jedoch idempotent. Sie führt das Handle nur beim ersten Aufruf aus und gibt bei nachfolgenden Aufrufen sofort das Ergebnis aus dem Cache zurück, ohne dass zusätzliche Nebenwirkungen auftreten.


$ mol_fiber: Idempotenz zuerst


Lassen Sie uns console.log in eine Glasfaser einwickeln, um sie idempotent zu machen, und sehen, wie sich das Programm verhält.


idempotente caches ausführen


Wie Sie sehen können, haben wir jetzt im Faserbaum Einträge für jeden Aufruf der log .


$ mol_fiber: zweite Idempotenz


Beim nächsten Neustart der walk Fiber führen wiederholte Aufrufe der log nicht mehr zu Aufrufen der realen console.log , aber sobald wir zur Ausführung der Fibers mit einem leeren Cache gelangen, werden die Aufrufe der console.log fortgesetzt.


Wiederverwendung idempotenter Caches


Bitte beachten Sie, dass in der Konsole jetzt nichts Überflüssiges angezeigt wird - genau das, was im synchronen Code ohne Faser und Quantifizierung angezeigt würde.


$ mol_fiber: Pause


Wie unterbricht die Berechnung? Zu Beginn des Quantums wird eine Frist festgelegt. Und bevor jede Faser gestartet wird, wird geprüft, ob wir sie erreicht haben. Und wenn Sie erreichen, dann eilt Promise , das im nächsten Frame aufgelöst wird und ein neues Quantum startet.


 if( Date.now() > $mol_fiber.deadline ) { throw new Promise( $mol_fiber.schedule ) } 

$ mol_fiber: Frist


Die Frist für das Quantum ist einfach festzulegen. Zur aktuellen Zeit werden 8 Millisekunden hinzugefügt. Warum genau 8, weil es bis zu 16 gibt, um den Schuss vorzubereiten? Tatsache ist, dass wir nicht im Voraus wissen, wie lange der Browser rendern muss, sodass wir etwas Zeit einplanen müssen, damit er funktioniert. Aber manchmal kommt es vor, dass der Browser nichts rendern muss, und dann können wir mit 8-ms-Quanten ein weiteres Quantum in denselben Frame einfügen, was eine dichte Packung von Quanten mit minimalen Prozessorausfallzeiten ergibt.


 const now = Date.now() const quant = 8 const elapsed = Math.max( 0 , now - $mol_fiber.deadline ) const resistance = Math.min( elapsed , 1000 ) / 10 // 0 .. 100 ms $mol_fiber.deadline = now + quant + resistence 

Wenn wir jedoch alle 8 ms eine Ausnahme auslösen, wird das Debuggen mit aktiviertem Ausnahmestopp zu einem kleinen Zweig der Hölle. Wir brauchen einen Mechanismus, um diesen Debugger-Modus zu erkennen. Leider kann dies nur indirekt verstanden werden: Eine Person braucht ungefähr eine Sekunde, um zu verstehen, ob sie die Ausführung fortsetzen soll oder nicht. Dies bedeutet, dass entweder das Debugger gestoppt wurde oder eine umfangreiche Berechnung durchgeführt wurde, wenn das Steuerelement längere Zeit nicht zum Skript zurückkehrte. Um auf beiden Stühlen zu sitzen, addieren wir 10% der verstrichenen Zeit zum Quantum, jedoch nicht mehr als 100 ms. Dies hat keinen großen Einfluss auf die FPS, verringert jedoch die Stoppfrequenz des Debuggers aufgrund der Quantisierung um eine Größenordnung.


Debug: versuchen / fangen


Was denken Sie, an welcher Stelle dieses Codes stoppt der Debugger, da wir über das Debuggen sprechen?


 function foo() { throw new Error( 'Something wrong' ) // [1] } try { foo() } catch( error ) { handle( error ) throw error // [2] } 

In der Regel muss er dort anhalten, wo die Ausnahme zum ersten Mal ausgelöst wird. In Wirklichkeit stoppt er jedoch nur dort, wo sie das letzte Mal ausgelöst wurde, was normalerweise sehr weit von dem Ort entfernt ist, an dem sie aufgetreten ist. Um das Debuggen nicht zu erschweren, sollten Ausnahmen daher niemals durch Try-Catch abgefangen werden. Aber auch ohne Ausnahmebehandlung ist es unmöglich.


Debug: nicht behandelte Ereignisse


In der Regel stellt eine Laufzeit ein globales Ereignis bereit, das für jede nicht erfasste Ausnahme auftritt.


 function foo() { throw new Error( 'Something wrong' ) } window.addEventListener( 'error' , event => handle( event.error ) ) foo() 

Zusätzlich zur Umständlichkeit hat diese Lösung einen solchen Nachteil, dass alle Ausnahmen hier fallen und es ziemlich schwierig ist zu verstehen, von welcher Faser und Faser das Ereignis aufgetreten ist.


Debug: Versprechen


Versprechen sind der beste Weg, um mit Ausnahmen umzugehen.


 function foo() { throw new Error( 'Something wrong' ) } new Promise( ()=> { foo() } ).catch( error => handle( error ) ) 

Die an Promise übergebene Funktion wird sofort synchron aufgerufen, aber die Ausnahme wird nicht abgefangen und stoppt den Debugger sicher an der Stelle seines Auftretens. Wenig später wird asynchron bereits der Fehlerhandler aufgerufen, in dem wir genau wissen, welche Glasfaser den Fehler verursacht hat und welcher Fehler. Dies ist genau der Mechanismus, der in $ mol_fiber verwendet wird.


Stapelspur: Faser reagieren


Werfen wir einen Blick auf die Stapelverfolgung, die Sie in React Fibre erhalten.


Leerer Grundnahrungsmittel


Wie Sie sehen können, bekommen wir viel Darmreaktion. Von dem hier nützlichen sind nur der Punkt des Auftretens der Ausnahme und die Namen der Komponenten in der Hierarchie höher. Nicht viel.


Stapelverfolgung: $ mol_fiber


In $ mol_fiber erhalten wir eine viel nützlichere Stapelverfolgung: keine Eingeweide, nur bestimmte Punkte im Anwendungscode, durch die es zu einer Ausnahme kam.


Inhalt strace


Dies wird durch die Verwendung des nativen Stapels, Versprechen und die automatische Entfernung des Darms erreicht. Wenn Sie möchten, können Sie den Fehler in der Konsole wie im Screenshot erweitern und die Eingeweide sehen, aber es gibt nichts Interessantes.


$ mol_fiber: behandeln


Um ein Quantum zu unterbrechen, wird Promise geworfen.


 limit() { if( Date.now() > $mol_fiber.deadline ) { throw new Promise( $mol_fiber.schedule ) } // ... } 

Aber wie Sie sich vorstellen können, kann Promise absolut alles sein - für eine Glasfaser spielt es im Allgemeinen keine Rolle, was zu erwarten ist: der nächste Frame, der Abschluss des Datenladens oder etwas anderes.


 fail( error : Error ) { if( error instanceof Promise ) { const listener = ()=> self.start() return error.then( listener , listener ) } // ... } 

Fibre abonniert einfach, um Versprechen zu lösen und neu zu starten. Das manuelle Werfen und Fangen von Versprechungen ist jedoch nicht erforderlich, da das Paket mehrere nützliche Wrapper enthält.


$ mol_fiber: Funktionen


Um eine synchrone Funktion in eine idempotente Faser zu verwandeln, wickeln Sie sie einfach in $mol_fiber_func .


 import { $mol_fiber_func as fiberize } from 'mol_fiber/web' const log = fiberize( console.log ) export const main = fiberize( ()=> { log( getData( 'goo.gl' ).data ) } ) 

Hier haben wir console.log idempotent gemacht und main gelernt zu unterbrechen, während wir auf den Download warten.


$ mol_fiber: Fehlerbehandlung


Aber wie soll man auf Ausnahmen reagieren, wenn wir try-catch nicht verwenden wollen? Dann können wir den Fehlerhandler mit $mol_fiber_catch ...


 import { $mol_fiber_func as fiberize , $mol_fiber_catch as onError } from 'mol_fiber' const getConfig = fiberize( ()=> { onError( error => ({ user : 'Anonymous' }) ) return getData( '/config' ).data } ) 

Wenn wir etwas anderes als den darin enthaltenen Fehler zurückgeben, ist dies das Ergebnis der aktuellen Faser. In diesem Beispiel gibt die Funktion getConfig die Konfiguration standardmäßig zurück, wenn es nicht möglich ist, die Konfiguration vom Server herunterzuladen.


$ mol_fiber: Methoden


Natürlich können Sie nicht nur Funktionen, sondern auch Methoden mit einem Dekorateur umschließen.


 import { $mol_fiber_method as action } from 'mol_fiber/web' export class Mover { @action move() { sendData( 'ya.ru' , getData( 'goo.gl' ) ) } } 

Hier haben wir beispielsweise Daten von Google hochgeladen und auf Yandex hochgeladen.


$ mol_fiber: verspricht


Um Daten vom Server herunterzuladen, reicht es beispielsweise aus, die asynchrone Funktion fetch und mit einem Handgriff synchron zu machen.


 import { $mol_fiber_sync as sync } from 'mol_fiber/web' export const getData = sync( fetch ) 

Diese Implementierung ist für alle gut, unterstützt jedoch nicht das Abbrechen einer Anforderung, wenn ein Faserbaum zerstört wird. Daher müssen wir eine verwirrendere API .


$ mol_fiber: Anfrage abbrechen


 import { $mol_fiber_async as async } from 'mol_fiber/web' function getData( uri : string ) : Response { return async( back => { var controller = new AbortController(); fetch( uri , { signal : controller.signal } ).then( back( res => res ) , back( error => { throw error } ) , ) return ()=> controller.abort() } ) } 

Die an den async Wrapper übergebene Funktion wird nur einmal aufgerufen, und der back Wrapper wird an ihn übergeben, in dem Sie die Rückrufe umbrechen müssen. Dementsprechend müssen Sie in diesen Rückrufen entweder den Wert zurückgeben oder eine Ausnahme auslösen. Was auch immer das Ergebnis des Rückrufs ist, es wird auch das Ergebnis der Faser sein. Bitte beachten Sie, dass wir am Ende eine Funktion zurückgeben, die bei vorzeitiger Zerstörung der Faser aufgerufen wird.


$ mol_fiber: Antwort abbrechen


Auf der Serverseite kann es auch nützlich sein, die Berechnung abzubrechen, wenn der Client heruntergefallen ist. Implementieren wir einen Wrapper über midleware , der eine Faser erstellt, in der die ursprüngliche midleware wird. , , , .


 import { $mol_fiber_make as Fiber } from 'mol_fiber' const middle_fiber = middleware => ( req , res ) => { const fiber = Fiber( ()=> middleware( req , res ) ) req.on( 'close' , ()=> fiber.destructor() ) fiber.start() } app.get( '/foo' , middle_fiber( ( req , res ) => { // do something } ) ) 

$mol_fiber: concurrency


, . , 3 : , , - ..


Schnelle und langsame Anfragen


: , . . , , .


$mol_fiber: properties


, ..


Pros:
  • Runtime support isn't required
  • Can be cancelled at any time
  • High FPS
  • Concurrent execution
  • Debug friendly
  • ~ 3KB gzipped


Cons:
  • Instrumentation is required
  • All code should be idempotent
  • Longer total execution

$mol_fiber — , . — , . , , . , , , , . , . .


Links



Call back


Ruckkopplung


: , , )


: , .


: . , .


: . , . , .


: , . , )


: , .


: - . , , .


: . , , .


: , . 16ms, ? 16 8 , 8, . , . , «».


: — . Vielen Dank!


: . , . Liebte es!


: , . .


: , , , , , / , .


: , .


: .


: , . mol.


: , , . , , , .


: .


: , . , $mol, , .


: , , . — . .


: - , .


: $mol , . (pdf, ) , .


: , . , .


: , ) .


: . .


: In some places I missed what the reporter was saying. The conversation was about how to use the "Mola" library and "why?". But how it works remains a mystery for me.To smoke an source code is for the overhead.


: , .


: . , . . .


: : . - (, ). , : 16?


: . . , mol_fiber … , 30fps 60fps — . — .

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


All Articles