Abschnitt 6 · Lektion 1
Warum asynchron?
Stell dir eine Person an einem Tresen vor. Sie kann immer nur eine Sache gleichzeitig tun. Aber sie kann Aufträge weitergeben: den Zettel in die Küche reichen, das Paket ins Lager melden, und weiterarbeiten, während jemand anders das erledigt.
Genau so arbeitet Node. Dein Code läuft in einem Thread, von oben nach unten, eine Zeile nach der anderen. Alles, was Zeit braucht und nicht dein Code ist, läuft daneben: Dateizugriffe, Netzwerk, Zeitgeber, Datenbankabfragen. Node reicht sie weiter und macht so lange etwas anderes.
Der JavaScript-Kurs hat dir await beigebracht. Was dort fehlte, ist der Grund dafür. Hier kommt er.
Warten kostet nichts
Drei Abfragen, jede braucht 300 Millisekunden. Wenn du sie nacheinander abwartest, dauert es so lange, wie es klingt.
const abfrage = (name, ms) =>
new Promise((fertig) => setTimeout(() => fertig(`${name} fertig`), ms));
const start = performance.now();
const eins = await abfrage("Preise", 300);
const zwei = await abfrage("Lager", 300);
const drei = await abfrage("Versand", 300);
console.log(eins, zwei, drei);
const dauer = Math.round((performance.now() - start) / 300) * 300;
console.log(`Fertig nach etwa ${dauer} ms`); Neunhundert Millisekunden für drei Vorgänge, die nichts voneinander wissen. Der Thread hat die ganze Zeit nichts getan, außer dreimal hintereinander dazusitzen und zu warten.
Startest du alle drei, bevor du auf das erste Ergebnis wartest, sieht die Rechnung anders aus.
const abfrage = (name, ms) =>
new Promise((fertig) => setTimeout(() => fertig(`${name} fertig`), ms));
const start = performance.now();
const [eins, zwei, drei] = await Promise.all([
abfrage("Preise", 300),
abfrage("Lager", 300),
abfrage("Versand", 300),
]);
console.log(eins, zwei, drei);
const dauer = Math.round((performance.now() - start) / 300) * 300;
console.log(`Fertig nach etwa ${dauer} ms`); Dreihundert Millisekunden. Nicht weil Node schneller geworden wäre, sondern weil die drei Wartezeiten übereinanderliegen statt hintereinander. Das ist der Kern: Warten ist kein Arbeiten, und mehrere Wartezeiten passen gleichzeitig in denselben Thread.
Das ist auch die Antwort auf die Frage, warum ein Node-Server hundert gleichzeitige Anfragen verträgt, ohne hundert Threads zu haben. Die meisten dieser Anfragen warten gerade: auf die Datenbank, auf eine fremde Schnittstelle, auf die Festplatte. Wartende Anfragen kosten fast nichts. Ein Thread kann sehr viele davon nebeneinander offen halten.
Rechnen kostet alles
Jetzt die andere Seite, und die ist der wichtigste Satz dieses Abschnitts.
Sobald dein Code wirklich rechnet, steht alles andere. Kein Timer läuft, keine Anfrage wird angenommen, keine Datei wird fertig gelesen. Der eine Thread ist beschäftigt, und es gibt keinen zweiten, der einspringen könnte.
setTimeout(() => {
console.log("Der Timer war fuer 0 ms angemeldet");
}, 0);
const bis = performance.now() + 500;
while (performance.now() < bis) {
// rechnet und gibt nicht ab
}
console.log("Rechnen fertig"); Der Timer war für null Millisekunden angemeldet, also für „so früh wie möglich”. Er kommt trotzdem erst nach der Rechenschleife dran, weil vorher niemand ihn aufrufen kann. Die Schleife gibt nicht ab.
Auf einem Server ist genau das der Unterschied zwischen einem Nutzer, der wartet, und neunundneunzig, die auch warten. Eine Anfrage, die eine halbe Sekunde rechnet, hält alle anderen eine halbe Sekunde auf. Und das sieht man nie, solange man allein testet: Bei einem einzigen Nutzer gibt es niemanden, den man aufhalten könnte.
Der Unterschied, auf den es ankommt
Warten und Arbeiten fühlen sich beim Schreiben gleich an. Beide stehen als eine Zeile da, beide brauchen Zeit. Für Node sind es zwei völlig verschiedene Dinge.
Warten heißt: Node hat den Auftrag abgegeben und macht in der Zwischenzeit etwas anderes. Eine Datei lesen, eine Anfrage stellen, einen Timer setzen. Davon kannst du sehr viele gleichzeitig laufen haben.
Arbeiten heißt: dein Code rechnet. Eine große Liste sortieren, ein riesiges JSON zerlegen, ein Bild umwandeln, eine Schleife über eine Million Einträge. Davon geht immer nur eins, und alles andere steht still.
await hilft gegen das Erste und gegen das Zweite gar nichts. await beschleunigt nämlich
überhaupt nichts, es gibt nur ab: Es sagt Node, dass hier gewartet wird und der Thread bis auf
Weiteres frei ist. Vor einer Rechenschleife hat es nichts abzugeben, also ändert es auch nichts.
Damit hast du das Modell. In Lektion 6.2 siehst du, in welcher Reihenfolge Node die weitergegebenen Aufträge wieder aufsammelt, und in Lektion 6.5 kommen wir auf die Rechenseite zurück und schauen uns an, welche Aufrufe im Alltag heimlich blockieren.
Zum Mitnehmen
Node hat für deinen Code genau einen Thread. Warten kostet ihn nichts, rechnen legt alles andere still.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.
Was in dieser Lektion steckt
-
Artikel mit 3 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.