Aufwärmen · Aufgabe 1 von 7
Zum Aufwärmen aus Lektion I1.2. Was gibt dieser Code aus?
Promise.resolve(5)
.then((n) => n * 2)
.then((n) => console.log(n));// I1.5 · ca. 33 Min. · Aufbau
Nach dieser Lektion verwandeln Sie einen Timer, eine Callback-API oder ein einmaliges Ereignis in eine Funktion, die ein Promise zurückgibt, sodass der restliche Code einfach darauf warten kann.
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
Promise.resolve(5)
.then((n) => n * 2)
.then((n) => console.log(n));Vorhersagen · Aufgabe 2 von 7
console.log("1");
const p = new Promise((resolve) => {
console.log("2");
resolve();
});
p.then(() => console.log("4"));
console.log("3");Üben · Aufgabe 3 von 7
function alarm(person, delay) {
return new Promise((resolve, reject) => {
if (delay < 0) {
____(new Error("negative delay"));
return;
}
setTimeout(() => resolve(`Wake up, ${person}!`), delay);
});
}Üben · Aufgabe 4 von 7
Üben · Aufgabe 5 von 7
const p = new Promise((resolve, reject) => {
resolve("first");
reject(new Error("second"));
resolve("third");
});
p.then((v) => console.log("fulfilled:", v), (e) => console.log("rejected:", e.message));Denksport · Aufgabe 6 von 7
const p = new Promise((resolve) => {
resolve(Promise.reject(new Error("inner")));
});Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
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
Jeder Block verwandelt eine andere Art asynchroner Arbeit in ein Promise. wait() packt setTimeout ein. translate() packt einen Error-first-Callback ein: reject bei einem Fehler, resolve mit dem Ergebnis. once() macht den nächsten Klick zu einem Promise; der Listener wird vor dem Klick hinzugefügt. withResolvers lässt einen Timer resolve von außen aufrufen, und Promise.try macht aus einem synchronen throw eine Ablehnung.
main.js
// 1. A timer: wait(ms, value) fulfils with value after ms milliseconds.
const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));
// 2. A callback API with error-first callbacks (a fake dictionary).
function lookUp(word, callback) {
setTimeout(() => {
if (word === "cat") callback(null, "Katze");
else callback(new Error(`no entry for ${word}`));
}, 10);
}
function translate(word) {
return new Promise((resolve, reject) => {
lookUp(word, (error, result) => {
if (error) reject(error);
else resolve(result);
});
});
}
// 3. A one-time event.
const once = (target, type) =>
new Promise((resolve) => target.addEventListener(type, resolve, {once: true}));
console.log(await wait(10, "timer done"));
console.log("cat:", await translate("cat"));
try {
await translate("dog");
} catch (error) {
console.log("dog:", error.message);
}
const button = new EventTarget();
const clicked = once(button, "click"); // listen first
setTimeout(() => button.dispatchEvent(new Event("click")), 10);
console.log("event:", (await clicked).type);
// 4. withResolvers: resolve from outside an executor.
const {promise, resolve} = Promise.withResolvers();
setTimeout(() => resolve("resolved from outside"), 10);
console.log(await promise);
// 5. Promise.try: a function that may throw before it returns anything.
const parse = (text) => JSON.parse(text);
console.log("try:", await Promise.try(parse, "{bad").catch((error) => error.name));
Ausführen mit
node main.jsAusgabe
timer done
cat: Katze
dog: no entry for dog
event: click
resolved from outside
try: SyntaxErrorTab 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.
Übung 1 von 2
config.js hat readConfig(name, callback), das mit (null, value) zurückruft, oder bei einem unbekannten Namen mit einem Error. Schreiben Sie readConfigAsync(name) so, dass es ein Promise zurückgibt: erfüllt mit dem Wert oder abgelehnt mit dem Fehler von readConfig. Der Startcode gibt aus dem Callback etwas zurück, was dem Aufrufer nichts liefert.
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.
Ein return im Callback geht an readConfig zurück, nicht an Ihren Aufrufer. Was sollte readConfigAsync stattdessen zurückgeben?
Geben Sie new Promise((resolve, reject) => { … }) zurück und rufen Sie readConfig darin auf.
Im Callback: if (error) reject(error); else resolve(value);
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
import {readConfig} from "./config.js";
export function readConfigAsync(name) {
return new Promise((resolve, reject) => {
readConfig(name, (error, value) => {
if (error) reject(error);
else resolve(value);
});
});
}
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
import {readConfig} from "./config.js";
export function readConfigAsync(name) {
return readConfig(name, (error, value) => value);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {readConfigAsync} from './main.js';
test('readConfigAsync gibt ein Promise zurück', () => {
const result = readConfigAsync('theme');
assert.ok(result instanceof Promise, `readConfigAsync gab ${result} zurück, kein Promise`);
});
test('Ein bekannter Name wird mit seinem Wert erfüllt', async () => {
const got = await readConfigAsync('size');
assert.equal(got, 14, `readConfigAsync('size') gab ${got}`);
});
test('Ein unbekannter Name wird mit dem Fehler des Callbacks abgelehnt', async () => {
await assert.rejects(readConfigAsync('font'), {message: 'no config named font'}, 'readConfigAsync sollte bei einem unbekannten Namen ablehnen');
});
config.js
// A callback API: calls back with (error, value) after 5 ms.
const configs = {theme: "dark", size: 14};
export function readConfig(name, callback) {
setTimeout(() => {
if (name in configs) callback(null, configs[name]);
else callback(new Error(`no config named ${name}`));
}, 5);
}
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.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 2 von 2
waitFor(target, type, ms) soll mit dem Ereignis erfüllt werden, wenn target innerhalb von ms Millisekunden type auslöst, und sonst mit einem Error abgelehnt werden, dessen Meldung no <type> within <ms> ms lautet. Der Timer im Startcode gibt den Error an resolve, ein fehlendes Ereignis sieht also wie ein Erfolg aus. Beheben Sie das.
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.
Führen Sie die Tests aus: Die Timeout-Fälle werden erfüllt. Welche der beiden Funktionen lässt ein Promise fehlschlagen?
resolve(new Error(…)) erfüllt mit einem Error-Objekt als Wert. Nur reject lässt das Promise ablehnen.
setTimeout(() => reject(new Error(`no ${type} within ${ms} ms`)), ms);
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export function waitFor(target, type, ms) {
return new Promise((resolve, reject) => {
target.addEventListener(type, resolve, {once: true});
setTimeout(() => reject(new Error(`no ${type} within ${ms} ms`)), ms);
});
}
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 function waitFor(target, type, ms) {
return new Promise((resolve, reject) => {
target.addEventListener(type, resolve, {once: true});
setTimeout(() => resolve(new Error(`no ${type} within ${ms} ms`)), ms);
});
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {waitFor} from './main.js';
test('Ein Ereignis in der Zeit erfüllt mit dem Ereignis', async () => {
const door = new EventTarget();
const opened = waitFor(door, 'open', 50);
setTimeout(() => door.dispatchEvent(new Event('open')), 5);
const event = await opened;
assert.equal(event.type, 'open', `waitFor wurde mit ${event} erfüllt`);
});
test('Kein Ereignis in der Zeit lehnt mit no open within 20 ms ab', async () => {
const door = new EventTarget();
await assert.rejects(waitFor(door, 'open', 20), {message: 'no open within 20 ms'}, 'waitFor sollte ablehnen, wenn das Ereignis nicht kommt');
});
test('Ein Ereignis nach dem Zeitlimit ändert das Ergebnis nicht', async () => {
const door = new EventTarget();
const late = waitFor(door, 'open', 10);
setTimeout(() => door.dispatchEvent(new Event('open')), 30);
await assert.rejects(late, {message: 'no open within 10 ms'}, 'das späte Ereignis sollte die Ablehnung nicht in einen Erfolg verwandeln');
});
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.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenfunction wait(ms) {
return new Promise(() => {
setTimeout(() => {
return "done";
}, ms);
});
}
console.log(await wait(10));
Was Node.js ausgibt
Warning: Detected unsettled top-level awaitWarum, und die Lösung
Das return geht an den Timer zurück, der es ignoriert, und der Executor bekommt resolve gar nicht erst als Parameter. Das Promise bleibt für immer offen, das Top-Level-await wird also nie fertig, und Node.js beendet sich mit Exit-Code 13. Nehmen Sie resolve als Parameter des Executors und rufen Sie es auf: new Promise((resolve) => setTimeout(() => resolve("done"), ms)).
function checkedWait(ms) {
return new Promise((resolve) => {
setTimeout(() => {
if (ms > 50) throw new Error("too long");
resolve(ms);
}, 0);
});
}
checkedWait(100)
.then((ms) => console.log("waited", ms))
.catch((error) => console.log("caught:", error.message));
Was Node.js ausgibt
Error: too longWarum, und die Lösung
Nur ein throw direkt im Executor lehnt das Promise ab. Dieses throw geschieht später, im Callback des Timers, nachdem der Executor zurückgekehrt ist, also sieht kein Promise es: Es ist ein nicht abgefangener Fehler, und das catch läuft nie. Melden Sie Fehler in Callbacks mit reject: Nehmen Sie reject als Parameter und rufen Sie reject(new Error("too long")); return auf.
const person = new Promise((resolve) => resolve("Ada", 36));
const [name, age] = await person;
console.log(name, age.toFixed(1));
Was Node.js ausgibt
TypeError: age.toFixed is not a functionWarum, und die Lösung
Ein Promise enthält genau einen Wert; resolve ignoriert jedes Argument nach dem ersten. person wird also mit dem Text "Ada" erfüllt, und das Destrukturieren eines Textes liefert seine Buchstaben: name ist "A", age ist "d". Packen Sie mehrere Werte in einen: resolve({name: "Ada", age: 36}) oder resolve(["Ada", 36]).
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.