| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/de/docs/Web/JavaScript/Guide/Using_promises | [Back] [Original] |
Get to know MDN better
Dieser Inhalt wurde automatisch aus dem Englischen bersetzt, und kann Fehler enthalten. Erfahre mehr ber dieses Experiment.
Ein Promise ist ein Objekt, das den spteren Erfolg oder Misserfolg einer asynchronen Operation reprsentiert. Da die meisten Entwickler bereits erstellte Promises verwenden, erklrt dieser Leitfaden zunchst, wie Sie mit zurckgegebenen Promises arbeiten, und anschlieend, wie Sie sie erstellen.
Im Wesentlichen ist ein Promise ein zurckgegebenes Objekt, an das Sie Callbacks anhngen, statt Callbacks an eine Funktion zu bergeben. Stellen Sie sich eine Funktion namens createAudioFileAsync() vor, die anhand eines Konfigurationsdatensatzes asynchron eine Audiodatei erzeugt und zwei Callback-Funktionen entgegennimmt: Eine wird aufgerufen, wenn die Datei erfolgreich erstellt wurde, die andere, wenn ein Fehler auftritt.
So knnte Code aussehen, der createAudioFileAsync() verwendet:
function successCallback(result) {
console.log(`Audio file ready at URL: ${result}`);
}
function failureCallback(error) {
console.error(`Error generating audio file: ${error}`);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback);
Wrde createAudioFileAsync() so umgeschrieben, dass die Funktion ein Promise zurckgibt, wrden Sie Ihre Callbacks stattdessen daran anhngen:
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
Diese Vorgehensweise hat mehrere Vorteile. Wir betrachten sie im Folgenden.
Hufig mssen zwei oder mehr asynchrone Operationen nacheinander ausgefhrt werden. Dabei beginnt jede weitere Operation, sobald die vorherige erfolgreich abgeschlossen wurde, und verwendet deren Ergebnis. Frher fhrte die Ausfhrung mehrerer asynchroner Operationen hintereinander zur klassischen Callback-Hlle:
doSomething(function (result) {
doSomethingElse(result, function (newResult) {
doThirdThing(newResult, function (finalResult) {
console.log(`Got the final result: ${finalResult}`);
}, failureCallback);
}, failureCallback);
}, failureCallback);
Mit Promises lsen wir das, indem wir eine Promise-Kette erstellen. Das API-Design von Promises eignet sich dafr besonders gut, weil Callbacks an das zurckgegebene Promise-Objekt angehngt und nicht an eine Funktion bergeben werden.
Der entscheidende Punkt ist: Die Funktion then() gibt ein neues Promise zurck, das sich vom ursprnglichen unterscheidet:
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
Dieses zweite Promise (promise2) reprsentiert nicht nur den Abschluss von doSomething(), sondern auch den Abschluss des bergebenen successCallback oder failureCallback. Dabei kann es sich wiederum um asynchrone Funktionen handeln, die ein Promise zurckgeben. In diesem Fall werden alle an promise2 angehngten Callbacks hinter dem Promise eingereiht, das successCallback oder failureCallback zurckgibt.
Hinweis: Wenn Sie ein funktionsfhiges Beispiel ausprobieren mchten, knnen Sie mit der folgenden Vorlage eine beliebige Funktion erstellen, die ein Promise zurckgibt:
function doSomething() {
return new Promise((resolve) => {
setTimeout(() => {
// Other things to do before completion of the promise
console.log("Did something");
// The fulfillment value of the promise
resolve("https://example.com/");
}, 200);
});
}
Die Implementierung wird weiter unten im Abschnitt Ein Promise um eine ltere Callback-API erstellen erlutert.
Mit diesem Muster knnen Sie lngere Verarbeitungsketten erstellen, in denen jedes Promise den Abschluss eines asynchronen Schritts reprsentiert. Auerdem sind die Argumente von then optional, und catch(failureCallback) ist eine Kurzform von then(null, failureCallback). Wenn Ihr Code zur Fehlerbehandlung fr alle Schritte gleich ist, knnen Sie ihn daher am Ende der Kette anhngen:
doSomething()
.then(function (result) {
return doSomethingElse(result);
})
.then(function (newResult) {
return doThirdThing(newResult);
})
.then(function (finalResult) {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
Dasselbe lsst sich auch mit Pfeilfunktionen ausdrcken:
doSomething()
.then((result) => doSomethingElse(result))
.then((newResult) => doThirdThing(newResult))
.then((finalResult) => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
Hinweis:
Pfeilfunktionsausdrcke knnen einen impliziten Rckgabewert haben. Daher ist () => x eine Kurzform von () => { return x; }.
doSomethingElse und doThirdThing knnen beliebige Werte zurckgeben. Wenn sie Promises zurckgeben, wird zunchst gewartet, bis das jeweilige Promise erfllt oder zurckgewiesen wurde. Der nchste Callback erhlt dann den Erfllungswert, nicht das Promise selbst. Es ist wichtig, Promises aus then-Callbacks immer zurckzugeben, auch wenn das Promise stets zu undefined aufgelst wird. Wenn der vorherige Handler ein Promise gestartet, aber nicht zurckgegeben hat, lsst sich dessen Abschluss nicht mehr nachverfolgen. Ein solches Promise wird als schwebend bezeichnet.
doSomething()
.then((url) => {
// Missing `return` keyword in front of fetch(url).
fetch(url);
})
.then((result) => {
// result is undefined, because nothing is returned from the previous
// handler. There's no way to know the return value of the fetch()
// call anymore, or whether it succeeded at all.
});
Wenn wir das Ergebnis des fetch-Aufrufs zurckgeben also ein Promise , knnen wir sowohl dessen Abschluss nachverfolgen als auch den resultierenden Wert erhalten.
doSomething()
.then((url) => {
// `return` keyword added
return fetch(url);
})
.then((result) => {
// result is a Response object
});
Schwebende Promises knnen bei Race Conditions noch problematischer sein: Wird das Promise des letzten Handlers nicht zurckgegeben, wird der nchste then-Handler zu frh aufgerufen. Ein Wert, den er liest, ist dann mglicherweise noch unvollstndig.
const listOfIngredients = [];
doSomething()
.then((url) => {
// Missing `return` keyword in front of fetch(url).
fetch(url)
.then((res) => res.json())
.then((data) => {
listOfIngredients.push(data);
});
})
.then(() => {
console.log(listOfIngredients);
// listOfIngredients will always be [], because the fetch request hasn't completed yet.
});
Als Faustregel gilt daher: Wenn Ihre Operation auf ein Promise stt, geben Sie es zurck und berlassen Sie seine Verarbeitung dem nchsten then-Handler.
const listOfIngredients = [];
doSomething()
.then((url) => {
// `return` keyword now included in front of fetch call.
return fetch(url)
.then((res) => res.json())
.then((data) => {
listOfIngredients.push(data);
});
})
.then(() => {
console.log(listOfIngredients);
// listOfIngredients will now contain data from fetch call.
});
Noch besser ist es, die verschachtelte Kette zu einer einzigen Kette abzuflachen. Das ist einfacher und erleichtert die Fehlerbehandlung. Einzelheiten finden Sie weiter unten im Abschnitt Verschachtelung.
doSomething()
.then((url) => fetch(url))
.then((res) => res.json())
.then((data) => {
listOfIngredients.push(data);
})
.then(() => {
console.log(listOfIngredients);
});
Mit async/await knnen Sie Code schreiben, der intuitiver ist und synchronem Code hnelt. Das folgende Beispiel zeigt denselben Ablauf mit async/await:
async function logIngredients() {
const url = await doSomething();
const res = await fetch(url);
const data = await res.json();
listOfIngredients.push(data);
console.log(listOfIngredients);
}
Beachten Sie, dass der Code bis auf die await-Schlsselwrter vor den Promises genauso aussieht wie synchroner Code. Einer der wenigen Nachteile ist, dass Sie das Schlsselwort await leicht vergessen knnen. Das fllt unter Umstnden erst bei einem Typkonflikt auf, etwa wenn Sie versuchen, ein Promise wie einen Wert zu verwenden.
async/await baut auf Promises auf. Beispielsweise ist doSomething() dieselbe Funktion wie zuvor, sodass fr den Wechsel von Promises zu async/await nur wenige nderungen erforderlich sind. Weitere Informationen zur Syntax von async/await finden Sie in den Referenzen zu asynchronen Funktionen und await.
Hinweis:
async/await hat dieselbe Semantik hinsichtlich gleichzeitiger Ausfhrung wie normale Promise-Ketten. await innerhalb einer asynchronen Funktion hlt nicht das gesamte Programm an, sondern nur die Teile, die vom erwarteten Wert abhngen. Whrend await auf ein Ergebnis wartet, knnen andere asynchrone Aufgaben weiterlaufen.
Vielleicht erinnern Sie sich daran, dass failureCallback in der zuvor gezeigten verschachtelten Callback-Struktur dreimal vorkam, am Ende der Promise-Kette dagegen nur einmal:
doSomething()
.then((result) => doSomethingElse(result))
.then((newResult) => doThirdThing(newResult))
.then((finalResult) => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);
Wenn eine Ausnahme auftritt, sucht der Browser entlang der Kette nach .catch()-Handlern oder onRejected. Das ist eng an die Funktionsweise von synchronem Code angelehnt:
try {
const result = syncDoSomething();
const newResult = syncDoSomethingElse(result);
const finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch (error) {
failureCallback(error);
}
Diese Parallele zu synchronem Code zeigt sich auch in der Syntax von async/await:
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch (error) {
failureCallback(error);
}
}
Promises beheben einen grundlegenden Mangel tief verschachtelter Callbacks, indem sie alle Fehler erfassen auch ausgelste Ausnahmen und Programmierfehler. Das ist fr die funktionale Komposition asynchroner Operationen unerlsslich. Alle Fehler werden nun von der Methode catch() am Ende der Kette behandelt. Ohne async/await sollten Sie try/catch daher fast nie bentigen.
In den obigen Beispielen mit listOfIngredients enthlt das erste Beispiel eine Promise-Kette, die im Rckgabewert eines anderen then()-Handlers verschachtelt ist. Das zweite Beispiel verwendet dagegen eine vollstndig flache Kette. Einfache Promise-Ketten sollten mglichst flach bleiben, denn Verschachtelung kann die Folge einer unachtsamen Komposition sein.
Verschachtelung ist eine Kontrollstruktur, mit der sich der Geltungsbereich von catch-Anweisungen begrenzen lsst. Ein verschachteltes catch erfasst nur Fehler innerhalb seines Geltungsbereichs und darunter, nicht jedoch Fehler weiter oben in der Kette auerhalb dieses Bereichs. Richtig eingesetzt ermglicht das eine gezieltere Fehlerbehandlung:
doSomethingCritical()
.then((result) =>
doSomethingOptional(result)
.then((optionalResult) => doSomethingExtraNice(optionalResult))
.catch((e) => {}),
) // Ignore if optional stuff fails; proceed.
.then(() => moreCriticalStuff())
.catch((e) => console.error(`Critical failure: ${e.message}`));
Beachten Sie, dass die optionalen Schritte hier verschachtelt sind. Die Verschachtelung ergibt sich nicht aus der Einrckung, sondern aus der Position der ueren Klammern ( und ) um diese Schritte.
Der innere catch-Handler, der Fehler unterdrckt, erfasst nur Fehler aus doSomethingOptional() und doSomethingExtraNice(). Danach wird der Code mit moreCriticalStuff() fortgesetzt. Schlgt dagegen doSomethingCritical() fehl, wird der Fehler nur vom abschlieenden (ueren) catch erfasst und nicht vom inneren catch-Handler unterdrckt.
Mit async/await sieht dieser Code so aus:
async function main() {
try {
const result = await doSomethingCritical();
try {
const optionalResult = await doSomethingOptional(result);
await doSomethingExtraNice(optionalResult);
} catch (e) {
// Ignore failures in optional steps and proceed.
}
await moreCriticalStuff();
} catch (e) {
console.error(`Critical failure: ${e.message}`);
}
}
Hinweis:
Wenn Sie keine differenzierte Fehlerbehandlung bentigen, brauchen Sie sehr wahrscheinlich auch keine verschachtelten then-Handler. Verwenden Sie stattdessen eine flache Kette und platzieren Sie die Fehlerbehandlung an deren Ende.
Sie knnen die Kette auch nach einem Fehler, also nach einem catch, fortsetzen. Das ist ntzlich, wenn Sie weitere Aktionen ausfhren mchten, obwohl eine vorherige Aktion in der Kette fehlgeschlagen ist. Betrachten Sie das folgende Beispiel:
doSomething()
.then(() => {
throw new Error("Something failed");
console.log("Do this");
})
.catch(() => {
console.error("Do that");
})
.then(() => {
console.log("Do this, no matter what happened before");
});
Es gibt den folgenden Text aus:
Do that Do this, no matter what happened before
Hinweis: Der Text Do this wird nicht angezeigt, weil der Fehler Something failed zur Zurckweisung gefhrt hat.
Mit async/await sieht dieser Code so aus:
async function main() {
try {
await doSomething();
throw new Error("Something failed");
console.log("Do this");
} catch (e) {
console.error("Do that");
}
console.log("Do this, no matter what happened before");
}
Wenn kein Handler die Zurckweisung eines Promise behandelt, wird sie bis an die Spitze des Aufrufstapels weitergegeben und muss von der Host-Umgebung sichtbar gemacht werden. Im Web wird bei jeder Zurckweisung eines Promise eines von zwei Ereignissen an den globalen Geltungsbereich gesendet. Im Allgemeinen ist das entweder window oder, bei Verwendung in einem Web Worker, Worker beziehungsweise eine andere Worker-basierte Schnittstelle. Die beiden Ereignisse sind:
unhandledrejectionWird gesendet, wenn ein Promise zurckgewiesen wird, aber kein Handler fr die Zurckweisung vorhanden ist.
rejectionhandledWird gesendet, wenn einem zurckgewiesenen Promise ein Handler hinzugefgt wird, nachdem es bereits ein unhandledrejection-Ereignis ausgelst hat.
In beiden Fllen hat das Ereignis vom Typ PromiseRejectionEvent eine promise-Eigenschaft, die das zurckgewiesene Promise angibt, sowie eine reason-Eigenschaft, die den Grund fr die Zurckweisung enthlt.
Damit knnen Sie eine allgemeine Fehlerbehandlung fr Promises bereitstellen und Probleme beim Umgang mit Promises leichter untersuchen. Diese Handler gelten global fr den jeweiligen Kontext. Unabhngig von ihrer Herkunft gelangen daher alle entsprechenden Fehler zu denselben Ereignishandlern.
In Node.js funktioniert die Behandlung zurckgewiesener Promises etwas anders. Nicht behandelte Zurckweisungen erfassen Sie, indem Sie einen Handler fr das Node.js-Ereignis unhandledRejection hinzufgen. Beachten Sie die andere Groschreibung im Namen:
process.on("unhandledRejection", (reason, promise) => {
// Add code here to examine the "promise" and "reason" values
});
Um in Node.js zu verhindern, dass der Fehler auf der Konsole protokolliert wird was andernfalls standardmig geschieht , gengt es, diesen process.on()-Listener hinzuzufgen. Ein Gegenstck zur Browser-Methode preventDefault() ist nicht erforderlich.
Wenn Sie diesen process.on-Listener hinzufgen, darin aber keinen Code zur Behandlung zurckgewiesener Promises vorsehen, werden diese lediglich stillschweigend ignoriert. Idealerweise prfen Sie deshalb im Listener jedes zurckgewiesene Promise und stellen sicher, dass die Zurckweisung nicht durch einen tatschlichen Programmfehler verursacht wurde.
Es gibt vier Werkzeuge zur Komposition, mit denen sich asynchrone Operationen gleichzeitig ausfhren lassen: Promise.all(), Promise.allSettled(), Promise.any() und Promise.race().
So knnen wir Operationen gleichzeitig starten und warten, bis alle abgeschlossen sind:
Promise.all([func1(), func2(), func3()]).then(([result1, result2, result3]) => {
// use result1, result2 and result3
});
Wird eines der Promises im Array zurckgewiesen, weist Promise.all() das zurckgegebene Promise sofort zurck. Die anderen Operationen laufen weiter, ihre Ergebnisse sind jedoch nicht ber den Rckgabewert von Promise.all() verfgbar. Das kann zu unerwarteten Zustnden oder Verhaltensweisen fhren. Promise.allSettled() ist ein weiteres Werkzeug zur Komposition, das sicherstellt, dass alle Operationen abgeschlossen sind, bevor das zurckgegebene Promise erfllt wird.
Bei all diesen Methoden laufen Promises gleichzeitig: Mehrere Promises werden zur selben Zeit gestartet und warten nicht aufeinander. Mit etwas geschickt eingesetztem JavaScript ist auch eine sequenzielle Komposition mglich:
[func1, func2, func3]
.reduce((p, f) => p.then(f), Promise.resolve())
.then((result3) => {
/* use result3 */
});
In diesem Beispiel verwenden wir reduce, um aus einem Array asynchroner Funktionen eine Promise-Kette zu bilden. Der obige Code entspricht:
Promise.resolve()
.then(func1)
.then(func2)
.then(func3)
.then((result3) => {
/* use result3 */
});
Daraus lsst sich eine wiederverwendbare Funktion zur Komposition erstellen, wie sie in der funktionalen Programmierung blich ist:
const applyAsync = (acc, val) => acc.then(val);
const composeAsync =
(...funcs) =>
(x) =>
funcs.reduce(applyAsync, Promise.resolve(x));
Die Funktion composeAsync() nimmt eine beliebige Anzahl von Funktionen als Argumente entgegen und gibt eine neue Funktion zurck. Diese nimmt einen Anfangswert entgegen, der die Kompositionskette durchluft:
const transformData = composeAsync(func1, func2, func3);
const result3 = transformData(data);
Mit async/await lsst sich die sequenzielle Komposition auch krzer ausdrcken:
let result;
for (const f of [func1, func2, func3]) {
result = await f(result);
}
/* use last result (i.e. result3) */
Bevor Sie Promises jedoch sequenziell verknpfen, prfen Sie, ob das wirklich ntig ist. Sofern die Ausfhrung eines Promise nicht vom Ergebnis eines anderen abhngt, ist es stets besser, sie gleichzeitig auszufhren, damit sie sich nicht unntig gegenseitig blockieren.
Promise selbst bietet kein eigenstndiges Protokoll zum Abbrechen. Mglicherweise knnen Sie jedoch die zugrunde liegende asynchrone Operation direkt abbrechen, blicherweise mit AbortController.
Sie knnen ein Promise mit seinem Konstruktor von Grund auf erstellen. Das sollte nur ntig sein, um ltere APIs einzubinden.
Im Idealfall wrden alle asynchronen Funktionen bereits Promises zurckgeben. Leider erwarten manche APIs noch immer, dass Callbacks fr Erfolg und/oder Fehler auf die herkmmliche Weise bergeben werden. Das naheliegendste Beispiel ist die Funktion setTimeout():
setTimeout(() => saySomething("10 seconds passed"), 10 * 1000);
Herkmmliche Callbacks mit Promises zu mischen, ist problematisch. Wenn saySomething() fehlschlgt oder einen Programmierfehler enthlt, wird dieser nicht abgefangen. Das liegt am Design von setTimeout().
Glcklicherweise knnen wir setTimeout() in ein Promise einbinden. Bewhrt hat sich, Funktionen, die Callbacks entgegennehmen, auf der niedrigstmglichen Ebene einzubinden und sie anschlieend nicht mehr direkt aufzurufen:
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
wait(10 * 1000)
.then(() => saySomething("10 seconds"))
.catch(failureCallback);
Der Promise-Konstruktor nimmt eine Executor-Funktion entgegen, mit der wir ein Promise selbst erfllen oder zurckweisen knnen. Da setTimeout() im eigentlichen Sinne nicht fehlschlgt, haben wir reject in diesem Fall weggelassen. Weitere Informationen zur Funktionsweise der Executor-Funktion finden Sie in der Referenz zu Promise().
Zum Schluss betrachten wir genauer, wann die registrierten Callbacks aufgerufen werden.
Bei einer Callback-basierten API hngt es von der Implementierung ab, wann und wie der Callback aufgerufen wird. Beispielsweise kann er synchron oder asynchron aufgerufen werden:
function doSomething(callback) {
if (Math.random() > 0.5) {
callback();
} else {
setTimeout(() => callback(), 1000);
}
}
Von diesem Design wird dringend abgeraten, weil es zum sogenannten State of Zalgo fhrt. Beim Entwurf asynchroner APIs bedeutet das, dass ein Callback in manchen Fllen synchron und in anderen asynchron aufgerufen wird. Fr den aufrufenden Code entsteht dadurch Unklarheit. Weitere Hintergrnde finden Sie im Artikel Designing APIs for Asynchrony, in dem der Begriff erstmals ausdrcklich vorgestellt wurde. Dieses API-Design erschwert die Analyse von Nebeneffekten:
let value = 1;
doSomething(() => {
value = 2;
});
console.log(value); // 1 or 2?
Promises sind dagegen eine Form der Inversion of Control: Die API-Implementierung bestimmt nicht, wann der Callback aufgerufen wird. Stattdessen bernimmt die Promise-Implementierung die Verwaltung der Callback-Warteschlange und entscheidet, wann die Callbacks aufgerufen werden. Dadurch erhalten sowohl diejenigen, die die API verwenden, als auch diejenigen, die sie entwickeln, automatisch verlssliche semantische Garantien, darunter:
then() hinzugefgte Callbacks werden niemals vor dem Abschluss des aktuellen Durchlaufs der JavaScript-Event-Loop aufgerufen.then() knnen mehrere Callbacks hinzugefgt werden. Sie werden nacheinander in der Reihenfolge aufgerufen, in der sie hinzugefgt wurden.Um berraschungen zu vermeiden, werden an then() bergebene Funktionen niemals synchron aufgerufen auch nicht bei einem bereits erfllten Promise:
Promise.resolve().then(() => console.log(2));
console.log(1);
// Logs: 1, 2
Statt sofort ausgefhrt zu werden, wird die bergebene Funktion in eine Microtask-Warteschlange eingereiht. Sie luft also spter: erst nachdem die Funktion, die sie erstellt hat, beendet wurde und der JavaScript-Ausfhrungsstapel leer ist, aber noch bevor die Steuerung an die Event-Loop zurckgegeben wird also schon bald:
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
wait(0).then(() => console.log(4));
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3));
console.log(1); // 1, 2, 3, 4
Promise-Callbacks werden als Microtasks verarbeitet, whrend Callbacks von setTimeout() ber die Task-Warteschlange verarbeitet werden.
const promise = new Promise((resolve, reject) => {
console.log("Promise callback");
resolve();
}).then((result) => {
console.log("Promise callback (.then)");
});
setTimeout(() => {
console.log("event-loop cycle: Promise (fulfilled)", promise);
}, 0);
console.log("Promise (pending)", promise);
Der obige Code gibt Folgendes aus:
Promise callback
Promise (pending) Promise {<pending>}
Promise callback (.then)
event-loop cycle: Promise (fulfilled) Promise {<fulfilled>}
Weitere Einzelheiten finden Sie unter Tasks vs. microtasks.
Wenn Promises und Tasks etwa Ereignisse oder Callbacks in einer unvorhersehbaren Reihenfolge ausgefhrt werden, kann es hilfreich sein, mit einem Microtask einen Zustand zu prfen oder den Ablauf von Promises auszugleichen, die nur unter bestimmten Bedingungen erstellt werden.
Wenn Microtasks bei der Lsung dieses Problems helfen knnten, lesen Sie den Leitfaden zu Microtasks. Dort erfahren Sie, wie Sie mit queueMicrotask() eine Funktion als Microtask einreihen.
Promiseasync functionawaitDer Bauplan fr ein besseres Internet.
Teile dieses Inhalts sind 19982026 von einzelnen mozilla.org-Mitwirkenden. Inhalte sind verfgbar unter einer Creative-Commons-Lizenz.
| Web Proxy Viewer | New URL | Original Page |