
Asynchrone Callbacks in JavaScript – kurz, knackig, und endlich verständlich

Judith
9. Februar 2019
Hinweis: Dieser Artikel wurde unter Zuhilfenahme von KI aus dem Englischen übersetzt. Hier geht's zum Originaltext.
Hast du manchmal das Gefühl, dein Gehirn hat zu viele Tabs gleichzeitig offen? Jedes Mal, wenn ich versuche, mehrere Dinge gleichzeitig zu erledigen, merke ich, dass ich eigentlich gar nicht gut im Multitasking bin. Die Entscheidung, welche Aufgabe als Erstes drankommt und was danach folgt, ist gar nicht so einfach. Als Entwickler:innen müssen wir vorher festlegen, was unser Programm eigentlich wann tun soll. Wie gehen wir also mit all den "zuerst... dann... und danach..." um, die zur Laufzeit auftreten?
Synchrone Funktionen
Behauptung: Immer wenn wir Code schreiben, agieren wir wie Architekt:innen, die einen Bauplan entwerfen für alles, was zur Laufzeit passieren soll. Ein Programm kann so schnell laufen, dass es aussieht, als ob viele Dinge gleichzeitig geschehen. Aber wenn wir die Ausführung verlangsamen, sehen wir: Die Engine arbeitet eine Zeile nach der anderen ab. Sie folgt dabei brav dem Bauplan. Alles, was zu erledigen ist, landet auf dem sogenannten "To-do"-Stapel der Engine. Hier ein einfaches Beispiel mit Funktionen, die Strings ausgeben:
Stellen wir uns vor, wir könnten das Programm in dem Moment pausieren, nachdem "second" ausgegeben wurde. Ein Blick hinter die Kulissen zeigt: Unsere Funktion third() liegt jetzt ganz oben auf dem Call Stack der Engine. Genau dort arbeitet sie weiter.

Quellcode (also unser Bauplan) und Engine (die ihn zur Laufzeit in Code-Happen verarbeitet): Jede Funktion ist so ein Code-Happen. Wird eine Funktion aufgerufen, landet ihr Inhalt ganz oben auf dem Call Stack. Die Engine ist keine Multitaskerin. Sie bearbeitet immer nur den obersten Happen. Danach geht's weiter mit dem nächsten. Das ist synchroner Code:
Synchron: Zeile 2 startet erst, wenn Zeile 1 vollständig abgeschlossen ist.
Synchrone Executor-Funktionen
Funktionen sind in JavaScript first-class citizens, d.h. wir können sie als Argumente übergeben:
Ob .map(), .forEach() oder .filter(): Die Methoden der Standardbibliothek sind synchron (Ausnahme: .setTimeout() und .setInterval(), die zur Window API gehören).
Die Funktion x => x * 2 wird sofort und mehrfach ausgeführt. Für jedes Element im Array landet die Funktion oben auf dem Stack, wird abgearbeitet, und danach geht's mit console.log() weiter. Solche "mitgegebenen Funktionen" nennen wir oft Callbacks — aber solange es synchron ist, nennen wir sie lieber Executor-Funktionen.
Asynchrone Funktionen
Manche Prozesse dauern einfach: Dateien lesen, Bilder verarbeiten, Infos abfragen. Unser Programm kann solche Aufgaben starten, weiß aber nicht, wann es eine Antwort bekommt.
Wichtig: Auch bei asynchronem Code ist JavaScript single-threaded. Keine Parallelverarbeitung! Aber wir können Aufgaben delegieren, z. B. an externe APIs oder Drittsysteme. Beispiel:
Das sieht harmlos aus, ist es aber nicht. Denn requestSync() blockiert unser Programm, bis der Server geantwortet hat. Alles friert ein.

Solche synchronen Funktionsaufrufe führen zu einer schlechten User Experience.
Asynchron: Wenn Zeile 1 (asynchrone Anweisung) gestartet wird, darf Zeile 2 schon weiterlaufen, auch wenn 1 noch nicht fertig ist. Das nennt man "Concurrency".
Jetzt probieren wir request() — eine (fiktive) asynchrone Variante:
Oops. Die Funktion request() startet die Anfrage, aber gibt sofort undefined zurück, weil die Antwort noch nicht da ist. Unser Code läuft zu schnell weiter.
Asynchrone Callbacks
Die Laufzeitumgebung hat dafür eine Lösung: die Task Queue. Sobald die Antwort da ist, landet ein Stück Code in dieser Warteschlange. Wie kommt es dahin? Indem wir beim Aufruf von request() eine Callback-Funktion mitgeben:
Was passiert hier? Die Callback-Funktion wird an den Drittanbieter übergeben. Sobald die Daten da sind, ruft dieser Code unsere Callback-Funktion auf — und die landet in der Task Queue. Erst nachdem der Call Stack leer ist, nimmt sich die Engine die Callback-Funktion vor.

Fazit
Der Unterschied zwischen synchronen Executor-Funktionen und asynchronen Callback-Funktionen liegt in der Laufzeit:
- Synchron: Funktion landet sofort auf dem Call Stack
- Asynchron: Funktion wird später in die Task Queue gelegt und wartet auf ihren Einsatz
Ich hoffe, dieser Artikel hat ein wenig Licht ins Dunkel gebracht. Früher galten Callbacks als der Standard für asynchrone Abläufe. Aber sie haben auch ihre Tücken. Wenn wir einem Dritten unsere Callback-Funktion überlassen, geben wir auch Kontrolle ab... Aber das ist Stoff für Teil 2: Dort erkläre ich, warum Promise und async/await echte Gamechanger sind.

Judith hat 2018 eine Ausbildung bei Peerigon zur Webentwicklerin begonnen. Als Teil ihrer wöchentlichen Routine veröffentlicht sie Blogposts über das, was sie in dieser Zeit lernt.
JavaScript
Asynchronous
Callbacks
Call Stack
Tutorial
Weitere Themen

Lea, 04.04.2025
Vue clever nutzen – Wiederverwendbarkeit für Einsteiger:innen
Vue
JavaScript
Reusability
DRY Principle
Components
Composables

Francesca, Ricarda, 02.04.2025
Die 10 häufigsten Fehler bei der Entwicklung digitaler Produkte – und wie du sie vermeidest
MVP development
UX/UI design
product vision
agile process
user engagement
product development

Judith, 31.03.2025