Async JS unter der Haube
MDN definiert den asynchronen Workflow als
..eine möglicherweise lange laufende Aufgabe starten und trotzdem auf andere Ereignisse reagieren können, während diese Aufgabe ausgeführt wird … Sobald diese Aufgabe abgeschlossen ist, wird Ihrem Programm das Ergebnis angezeigt.
Doch wie genau lässt sich das im Web mit JavaScript erreichen?
Bevor Sie sich mit asynchronen Implementierungen befassen, ist es hilfreich, ein grundlegendes Verständnis von JS auf einer niedrigeren Ebene zu haben.
Wie funktioniert JS?
JavaScript ist Single-Threaded und interpretiert, was bedeutet, dass Anweisungen direkt ausgeführt werden und keinen Ex-ante-Kompilierungsschritt erfordern. Nehmen wir als Beispiel den V8, den beliebtesten Motor.
Auf hoher Ebene analysiert V8 den Code, erstellt einen abstrakten Syntaxbaum (AST) und kompiliert ihn mit dem Interpreter zu optimiertem Bytecode. Es gibt auch einen Preparer, zwei Compiler und Caching, die es schnell machen (V8 hat tolle Blogs darüber). Es stellt uns nur einen Aufrufstapel, einen Heap und eine Ereignisschleife zur Verfügung ( irgendwie, nicht wirklich) . Wir können den Heap für die Zwecke dieses Kaninchenbaus ignorieren: Es handelt sich um einen Teil unstrukturierten Speichers mit Objektreferenzen und Variablen.
Laufzeitereignisschleife
Im Gegensatz zu Java oder C++ ermöglicht JS die Ausführung jeweils eines Ereignisses. Wir können dies in unseren Browsern sehen, indem wir einen Aufrufpfad verfolgen mit console.trace():
// shoutout to ES6 arrow functions
foo = () => {
console.log("Hello, World!");
bar();
}
bar = () => {
console.log("Goodbye, World!");
trace();
}
trace = () => console.trace();
foo();
Hello, World! VM1049:6
Goodbye, World! VM1049:11
// console.trace
trace @ VM1049:11
bar @ VM1049:7
foo @ VM1049:3
call stack visualization
SetTimeout
Lassen Sie uns die gleichen Methoden mit einer asynchronen Verzögerung von 5000 ms mit ausführen setTimeout().
foo = () => {
console.log("Hello, World!");
setTimeout(bar, 5000);
}
bar = () => {
console.log("Goodbye, World!");
trace();
}
trace = () => console.trace();
foo();
Hello, World! VM1526:2
// 3 days later...
Goodbye, World! VM1526:6
// console.trace
trace @ VM1382:10
bar @ VM1382:6
// setTimeout is invoked
b @ main-base.bundle.UM3…oFn_e5gQ.12.js:1723
setTimeout (async)
Cga.window.setTimeout @ main-base.bundle.UM3…toFn_e5gQ.12.js:516
// foo is pushed to the stack first
foo @ VM1382:3
setTimeoutist kein Teil der JS-Engine, was Sinn macht, da die Engine jeweils ein Ereignis verarbeiten kann. Browser sind jedoch Multithread-fähig und stellen uns Web-APIs für den Zugriff auf setTimeout-Implementierungen zur Verfügung. Beispielsweise verwendet Chrome die Single-Thread-V8-Engine in einer Multi-Prozess-Architektur, in der jeder Prozess stark multi-threaded ist. In diesem Fall wurde dies explizit mit Cga.setTimeout @ main-base.bundle.blah-blah-blah...Virtually aufgerufen, wodurch wir mehrere Threads verwenden können.
Die Ereignisschleife
Ordnen wir unseren Codeblock neu an:
foo = () => {
console.log("Hello, World!");
setTimeout(bar, 5000);
}
bar = () => {
setTimeout(() => console.log("Goodbye, World!"), 5000);
trace();
}
trace = () => console.trace();
foo();
// output of foo
Hello, World! VM1649:11
// console.trace
trace @ VM1694:11
bar @ VM1694:8
b @ main-base.bundle.UM3…oFn_e5gQ.12.js:1723
setTimeout (async)
Cga.window.setTimeout @ main-base.bundle.UM3…toFn_e5gQ.12.js:516
foo @ VM1694:3
// end trace
// 3 days later...
VM1649:6 Goodbye, World!
Die JRE umfasst:
- JS Engine (Single-Threaded)
- Java Virtual Machine (JVM) – Die virtuelle Maschine, die den Code zur Laufzeit ausführt. Übrigens, aus diesem Grund wird „VM“ den Trace-Protokollen vorangestellt.
- Web-API
- Aufgabenwarteschlange
- Ereignisschleife
Der Aufrufstapel unserer Engine löst die Ereignisse nacheinander aus. Sobald setTimeout() erkannt wird, wird die Methode zusammen mit dem Rückruf (Funktionsreferenz) an die Web-API delegiert.
Beachten Sie, dass es der Engine egal ist, wie die setTimeout-Implementierung der API das Ereignis und den Rückruf behandelt. Die Engine fährt fort, das nächste Ereignis einzublenden und es an Pacman (die JVM, die den Code ausführt) weiterzuleiten.
In der Zwischenzeit wartet die API mit ihren Threads 5000 ms und übergibt sie anschließend an die Task-Warteschlange. Die Ereignisschleife erkennt, dass das Ereignis in die Reihe kommt und schiebt es genau dann zurück auf den Aufrufstapel, wenn der Aufrufstapel leer ist.
Mit dem unterhaltsamen Visualizer von Philip Roberts können Sie alles in Aktion sehen. Schauen Sie sich auch seinen großartigen Vortrag auf der JSConf im Jahr 2014 an.

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































