Zum Inhalt springen
aviral gupta

// I1.3 · ca. 33 Min. · Aufbau

async, await und try/catch

Nach dieser Lektion schreiben Sie Promise-Code mit async und await, der sich von oben nach unten liest, fangen seine Fehler mit try/catch/finally ab und starten unabhängige Schritte gemeinsam statt nacheinander.

Lektion 3 von 6 in I1 Asynchrones JavaScript

Danach können Sie

  • Eine async-Funktion schreiben, Ergebnisse mit await lesen und sagen, was die Funktion zurückgibt
  • Eine Ablehnung mit try/catch um await abfangen und mit finally aufräumen
  • Entscheiden, ob Schritte nacheinander abgewartet oder gemeinsam gestartet werden
  1. Aufwärmen · Aufgabe 1 von 7

    Zum Aufwärmen: Was bekommen Sie, wenn Sie eine async-Funktion aufrufen?

    async function price() {
      return 5;
    }
    console.log(price());
  2. Vorhersagen · Aufgabe 2 von 7

    Sagen Sie es vorher, bevor Sie weiterlesen. b braucht 10 ms, a braucht 30 ms. Was gibt dieser Code aus?

    const later = (value, ms) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
    
    async function main() {
      console.log(await later("a", 30));
      console.log(await later("b", 10));
    }
    main();
    console.log("main called");
  3. Üben · Aufgabe 3 von 7

    Ergänzen Sie das Schlüsselwort, mit dem diese Funktion await benutzen darf.

    ____ function load() {
      const value = await Promise.resolve("data");
      return value;
    }
    function load() {
  4. Üben · Aufgabe 4 von 7

    Das abgewartete Promise wird abgelehnt. Was gibt dieser Code aus?

    async function save() {
      try {
        await Promise.reject(new Error("disk full"));
        console.log("saved");
      } catch (error) {
        console.log("error:", error.message);
      } finally {
        console.log("cleanup");
      }
    }
    save();
  5. Üben · Aufgabe 5 von 7

    Ordnen Sie in einer async-Funktion jedem Ausdruck zu, was er liefert.

  6. Denksport · Aufgabe 6 von 7

    Knobelaufgabe. isAdmin() gibt für Ada false zurück. Was gibt dieser Code aus?

    async function isAdmin(user) {
      return user === "root";
    }
    
    if (isAdmin("ada")) {
      console.log("welcome, admin");
    } else {
      console.log("welcome, user");
    }
  7. Anwenden · Aufgabe 7 von 7

    Kleine Aufgabe. Schreiben Sie async function loadPage(failSidebar), die spinner on ausgibt, einen Artikel (30 ms) und eine Seitenleiste (10 ms) gleichzeitig mit einer Promise-Hilfsfunktion wait(ms, value, fail) lädt, show article and sidebar ausgibt und in jedem Fall spinner off ausgibt. Schlägt die Seitenleiste fehl, geben Sie statt der show-Zeile error: und ihre Meldung aus. Führen Sie die Funktion einmal mit jedem Argument aus.

    Prüfen Sie Ihr Ergebnis anhand dieser Liste

Selbst programmieren

Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.

Ausgearbeitetes Beispiel

Nacheinander, gemeinsam und ein abgefangener Fehler

step() gibt aus, wann es startet und endet, und wird nach einer Verzögerung erledigt. oneByOne() wartet auf a, dann auf b: b startet erst, nachdem a geendet hat. together() startet c und d gleichzeitig und wartet mit Promise.all, das schnellere d endet also zuerst, die Ergebnisse behalten aber ihre Reihenfolge. safely() wartet in try auf einen Schritt, der fehlschlägt: catch und finally laufen. main.js ist ein Modul, es darf daher auf oberster Ebene warten.

main.js

// A step that logs when it starts and ends, and settles after ms milliseconds.
const step = (name, ms, fail = false) =>
  new Promise((resolve, reject) => {
    console.log(`start ${name}`);
    setTimeout(() => {
      console.log(`end ${name}`);
      if (fail) reject(new Error(`${name} failed`));
      else resolve(name.toUpperCase());
    }, ms);
  });

async function oneByOne() {
  const a = await step("a", 30); // b does not start until a has ended
  const b = await step("b", 10);
  return [a, b];
}

async function together() {
  const [c, d] = await Promise.all([step("c", 30), step("d", 10)]); // both start now
  return [c, d];
}

async function safely() {
  try {
    await step("e", 5, true);
    console.log("not reached");
  } catch (error) {
    console.log("caught:", error.message);
  } finally {
    console.log("finally runs");
  }
}

console.log(await oneByOne()); // top-level await: main.js is a module
console.log(await together());
await safely();

Ausführen mit

node main.js

Ausgabe

start a
end a
start b
end b
[ 'A', 'B' ]
start c
start d
end d
end c
[ 'C', 'D' ]
start e
end e
caught: e failed
finally runs
  • Nacheinander: start b kommt nach end a, die Wartezeiten addieren sich also.
  • Gemeinsam: Beide starten, bevor eines endet; das langsamere bestimmt die Dauer.
  • Promise.all behält die Reihenfolge der Eingabe: [ 'C', 'D' ], obwohl d zuerst endete.
  • „not reached“ wird übersprungen: Das await warf in das catch, danach lief finally.
Ändern und ausführen

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Übungen

Übung 1 von 2

await innerhalb von try

profileLine(api) gibt ein Promise auf eine Zeile wie Ada (3 posts) zurück. api.user() liefert {id, name}; api.posts(id) liefert ein Array von Beiträgen. Wird api.posts abgelehnt, lautet die Zeile Ada (posts unavailable); wird api.user abgelehnt, wird profileLine mit diesem Fehler abgelehnt. Im Startcode fehlt ein await, deshalb gibt er undefined als Beitragszahl aus und fängt nie etwas ab. Beheben Sie das mit await und try/catch.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Führen Sie die Tests aus: Der erste meldet undefined Beiträge. Was ist posts ohne await?

  2. Hinweis 2

    Ein Promise hat keine length, und eine Ablehnung, die innerhalb von try nie abgewartet wird, erreicht das catch nicht.

  3. Hinweis 3

    Schreiben Sie const posts = await api.posts(user.id); innerhalb von try.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export async function profileLine(api) {
  const user = await api.user();
  try {
    const posts = await api.posts(user.id);
    return `${user.name} (${posts.length} posts)`;
  } catch {
    return `${user.name} (posts unavailable)`;
  }
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export async function profileLine(api) {
  const user = await api.user();
  try {
    const posts = api.posts(user.id);
    return `${user.name} (${posts.length} posts)`;
  } catch {
    return `${user.name} (posts unavailable)`;
  }
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {profileLine} from './main.js';

const later = (value, fail = false) =>
  new Promise((resolve, reject) => setTimeout(() => (fail ? reject(value) : resolve(value)), 5));

test('Ada mit drei Beiträgen ergibt Ada (3 posts)', async () => {
  const api = {user: () => later({id: 1, name: 'Ada'}), posts: () => later(['a', 'b', 'c'])};
  const got = await profileLine(api);
  assert.equal(got, 'Ada (3 posts)', `profileLine gab ${got}`);
});

test('Schlagen die Beiträge fehl, ergibt es Ada (posts unavailable)', async () => {
  const api = {user: () => later({id: 1, name: 'Ada'}), posts: () => later(new Error('timeout'), true)};
  const got = await profileLine(api);
  assert.equal(got, 'Ada (posts unavailable)', `profileLine gab ${got}`);
});

test('Ein fehlschlagender Benutzer lehnt mit demselben Fehler ab', async () => {
  const api = {user: () => later(new Error('no user'), true), posts: () => later([])};
  await assert.rejects(profileLine(api), {message: 'no user'}, 'profileLine sollte mit dem Benutzerfehler ablehnen');
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Übung 2 von 2

Alle gleichzeitig starten

loadAll(loaders) bekommt ein Array von Funktionen; jede gibt ein Promise zurück. Es soll ein Promise auf alle Ergebnisse in derselben Reihenfolge zurückgeben. Der Startcode wartet in einer Schleife auf jeden Loader, jeder startet also erst, wenn der vorige fertig ist. Die Ergebnisse stimmen, aber es ist langsam. Starten Sie zuerst jeden Loader und warten Sie dann gemeinsam auf alle.

Tab rückt ein, Umschalt+Tab rückt aus. Um den Editor mit der Tastatur zu verlassen, drücken Sie Esc und dann Tab.

Beim ersten Ausführen lädt Ihr Browser den JavaScript-Runner herunter (bis zu 0.1 MB) und speichert ihn im Cache. Ihr Code läuft in der eigenen Engine Ihres Browsers und bleibt auf Ihrem Gerät.

Hinweise
  1. Hinweis 1

    Führen Sie die Tests aus: Das Protokoll zeigt start 0, end 0, start 1. Wo wartet die Schleife?

  2. Hinweis 2

    Rufen Sie zuerst jeden Loader auf: loaders.map((load) => load()) ergibt ein Array von Promises, die bereits laufen.

  3. Hinweis 3

    Warten Sie dann mit await Promise.all(…) auf alle gemeinsam; das behält ihre Reihenfolge.

Eine Lösung zeigen

Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.

export async function loadAll(loaders) {
  return Promise.all(loaders.map((load) => load()));
}
Auf dem eigenen Computer ausführen

Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.

main.js

export async function loadAll(loaders) {
  const results = [];
  for (const load of loaders) {
    results.push(await load());
  }
  return results;
}

main.test.js

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {loadAll} from './main.js';

// Each loader logs when it starts and ends; later ones are faster.
const makeLoaders = (log) =>
  [30, 20, 10].map((ms, i) => () => {
    log.push(`start ${i}`);
    return new Promise((resolve) => setTimeout(() => {
      log.push(`end ${i}`);
      resolve(`r${i}`);
    }, ms));
  });

test('Die Ergebnisse kommen in der Reihenfolge der Eingabe', async () => {
  const got = await loadAll(makeLoaders([]));
  assert.deepEqual(got, ['r0', 'r1', 'r2'], `loadAll gab ${JSON.stringify(got)}`);
});

test('Jeder Loader startet, bevor einer von ihnen endet', async () => {
  const log = [];
  await loadAll(makeLoaders(log));
  assert.deepEqual(log.slice(0, 3), ['start 0', 'start 1', 'start 2'], `die Reihenfolge war ${log.join(', ')}`);
});

package.json

{
  "type": "module"
}

package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.

Programm ausführen:

node main.js

Prüfungen ausführen (learnrun.js muss im selben Ordner liegen):

node --test
learnrun.js herunterladen

Häufige Fehler

await in einer Funktion, die nicht async ist

function loadName() {
  const name = await Promise.resolve("Ada");
  return name;
}
console.log(loadName());

Was Node.js ausgibt

SyntaxError: Unexpected reserved word

Warum, und die Lösung

In einem Modul ist await ein reserviertes Wort, eine gewöhnliche Funktion, die es benutzt, lässt sich also gar nicht übersetzen, und nichts läuft. Machen Sie die Funktion async: async function loadName() { … }. Denken Sie dann daran, dass ihr Aufrufer ein Promise bekommt und ebenfalls await loadName() schreiben muss.

Ein vergessenes await

async function getPrice() {
  return 4.5;
}
const total = getPrice() + 1;
console.log(total);
console.log(total.toFixed(2));

Was Node.js ausgibt

TypeError: total.toFixed is not a function

Warum, und die Lösung

getPrice() gibt ein Promise zurück, und + macht aus diesem Promise den Text [object Promise]. total ist also der Text "[object Promise]1", und Texte haben kein toFixed. Die erste Zeile gibt ihn ohne Fehler aus. Schreiben Sie const total = (await getPrice()) + 1;.

Zwei gestartete Promises, nacheinander abgewartet

const wait = (ms, fail) =>
  new Promise((resolve, reject) => setTimeout(() => (fail ? reject(new Error("fast one failed")) : resolve("ok")), ms));

async function both() {
  const slow = wait(50, false);
  const fast = wait(10, true);
  try {
    return [await slow, await fast];
  } catch (error) {
    return "handled: " + error.message;
  }
}
console.log(await both());

Was Node.js ausgibt

Error: fast one failed

Warum, und die Lösung

fast wird nach 10 ms abgelehnt, während die Funktion noch auf slow wartet, also behandelt noch nichts fast. Node.js wertet das als unbehandelte Ablehnung und beendet das Programm; das catch läuft nie, und „handled“ wird nie ausgegeben. Genau davor warnt MDN. Warten Sie auf beide gleichzeitig: return await Promise.all([slow, fast]); innerhalb von try.

JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode

Abschlussquiz

5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.

Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.

Problem melden

Etwas ist falsch oder unklar? Beschreiben Sie es kurz, dann wird es geprüft und korrigiert.

#

Mindestens 20 Zeichen.

Nur, wenn Sie eine Antwort wünschen.

Kernideen

async gibt ein Promise zurück, await liest es

Eine async-Funktion gibt immer ein Promise zurück: return 5 erfüllt es mit 5, ein throw lehnt es ab. Innen hält await p die Funktion an, bis p erledigt ist, und liefert dann den Wert oder wirft den Grund der Ablehnung. Der Code nach einem await läuft später, wie ein then()-Callback (Lektion I1.1). await funktioniert nur in einer async-Funktion oder auf oberster Ebene eines Moduls; main.js ist ein Modul, dort darf direkt gewartet werden. Wer einen einfachen Wert wie 42 abwartet, bekommt einfach 42, einen Moment später.

try, catch, finally um await

Weil await den Grund der Ablehnung wirft, behandeln gewöhnliche try/catch-Blöcke asynchrone Fehler: Der Rest des try-Blocks wird übersprungen, und catch bekommt den Fehler. finally läuft, ob etwas geworfen wurde oder nicht, und ist damit der Ort, um einen Ladekreisel auszuschalten oder eine Datei zu schließen. Ein catch sieht nur, was innerhalb seines try abgewartet wird. Fehlt das await, ist der Wert ein Promise: getPrice() + 1 ist der Text "[object Promise]1", und if (isAdmin(user)) ist immer wahr, weil ein Promise-Objekt als wahr gilt.

Nacheinander oder gemeinsam

const a = await stepA(); const b = await stepB(); startet stepB erst, wenn stepA fertig ist, die Zeiten addieren sich also. Das ist richtig, wenn B das Ergebnis von A braucht. Sind die Schritte unabhängig, starten Sie sie zuerst und warten auf alle: await Promise.all([stepA(), stepB()]) dauert so lange wie der langsamste. Starten Sie nicht zwei Promises und warten dann nacheinander auf sie: Wird das zweite abgelehnt, während das erste noch aussteht, ist die Ablehnung laut MDN unbehandelt, egal welches catch der Aufrufer hat.

Quellen

Zuletzt geprüft am 5. Oktober 2026