Suche…
Syntax
- neues Versprechen (/ * Executor-Funktion: * / Funktion (Auflösen, Ablehnen) {})
- promise.then (onFulfilled [, onRejected])
- promise.catch (onRejected)
- Promise.resolve (Auflösung)
- Promise.reject (Grund)
- Promise.all (iterable)
- Promise.race (iterable)
Bemerkungen
Versprechungen sind Teil der ECMAScript 2015-Spezifikation und die Browser-Unterstützung ist begrenzt. 88% der Browser weltweit unterstützen sie seit Juli 2017. Die folgende Tabelle gibt einen Überblick über die ersten Browserversionen, die Versprechen unterstützen.
Chrom | Kante | Feuerfuchs | Internet Explorer | Oper | Opera Mini | Safari | iOS Safari |
---|---|---|---|---|---|---|---|
32 | 12 | 27 | x | 19 | x | 7.1 | 8 |
In Umgebungen, in denen sie nicht unterstützt werden, kann Promise
polyfilled werden. Bibliotheken von Drittanbietern bieten möglicherweise auch erweiterte Funktionen, z. B. automatisierte "Bekanntmachung" von Rückruffunktionen oder zusätzliche Methoden wie progress
auch als notify
.
Die Promises / A + -Standardwebsite enthält eine Liste von 1.0- und 1.1-kompatiblen Implementierungen . Promise Callbacks basierend auf dem A + -Standard werden immer asynchron als Mikrotasks in der Ereignisschleife ausgeführt .
Versprechen Verkettung
Die then
Methode eines Versprechens gibt ein neues Versprechen zurück.
const promise = new Promise(resolve => setTimeout(resolve, 5000));
promise
// 5 seconds later
.then(() => 2)
// returning a value from a then callback will cause
// the new promise to resolve with this value
.then(value => { /* value === 2 */ });
Eine Wiederkehr Promise
von einem then
Rückruf wird es an die Versprechen Kette anhängen.
function wait(millis) {
return new Promise(resolve => setTimeout(resolve, millis));
}
const p = wait(5000).then(() => wait(4000)).then(() => wait(1000));
p.then(() => { /* 10 seconds have passed */ });
Ein catch
erlaubt es einem abgelehnten Versprechen, sich zu erholen, ähnlich wie catch
in einer try
/ catch
Anweisung funktioniert. Jede gekettet then
nach einem catch
seiner Entschlossenheit Handler ausführt , den Wert aus dem aufgelösten mit catch
.
const p = new Promise(resolve => {throw 'oh no'});
p.catch(() => 'oh yes').then(console.log.bind(console)); // outputs "oh yes"
Wenn sich in der Mitte der Kette keine catch
oder reject
, erfasst ein catch
am Ende jede Ablehnung in der Kette:
p.catch(() => Promise.reject('oh yes'))
.then(console.log.bind(console)) // won't be called
.catch(console.error.bind(console)); // outputs "oh yes"
In bestimmten Fällen möchten Sie möglicherweise die Ausführung der Funktionen "verzweigen". Sie können dies tun, indem Sie je nach Bedingung verschiedene Versprechen einer Funktion zurückgeben. Später im Code können Sie alle diese Zweige zu einem Zweig zusammenführen, um andere Funktionen darauf aufzurufen und / oder alle Fehler an einer Stelle zu behandeln.
promise
.then(result => {
if (result.condition) {
return handlerFn1()
.then(handlerFn2);
} else if (result.condition2) {
return handlerFn3()
.then(handlerFn4);
} else {
throw new Error("Invalid result");
}
})
.then(handlerFn5)
.catch(err => {
console.error(err);
});
Die Ausführungsreihenfolge der Funktionen sieht daher folgendermaßen aus:
promise --> handlerFn1 -> handlerFn2 --> handlerFn5 ~~> .catch()
| ^
V |
-> handlerFn3 -> handlerFn4 -^
Der einzelne catch
erhält den Fehler in dem Zweig, in dem er auftritt.
Einführung
Ein Promise
Objekt stellt eine Operation dar, die einen Wert erzeugt hat oder schließlich produzieren wird. Versprechungen bieten eine robuste Methode, um das (möglicherweise anstehende) Ergebnis asynchroner Arbeit einzuwickeln, wodurch das Problem tief verschachtelter Rückrufe (als " Rückruf-Hölle " bezeichnet) gemildert wird .
Zustände und Kontrollfluss
Ein Versprechen kann in einem von drei Zuständen sein:
- ausstehend - Der zugrunde liegende Vorgang ist noch nicht abgeschlossen, und das Versprechen steht noch aus .
- erfüllt - Die Operation ist beendet und das Versprechen wird mit einem Wert erfüllt . Dies ist analog zur Rückgabe eines Wertes von einer synchronen Funktion.
- Abgelehnt - Während der Operation ist ein Fehler aufgetreten, und das Versprechen wird mit einem Grund abgelehnt . Dies ist analog zum Auslösen eines Fehlers in einer Synchronfunktion.
Ein Versprechen soll geklärt werden (oder gelöst) , wenn sie entweder erfüllt oder abgelehnt wird. Sobald ein Versprechen erfüllt ist, wird es unveränderlich und sein Zustand kann sich nicht ändern. Die then
und catch
Methoden eines Versprechens können verwendet werden, um Callbacks anzuhängen, die ausgeführt werden, wenn sie abgerechnet werden. Diese Rückrufe werden mit dem Erfüllungswert bzw. dem Ablehnungsgrund aufgerufen.
Beispiel
const promise = new Promise((resolve, reject) => {
// Perform some work (possibly asynchronous)
// ...
if (/* Work has successfully finished and produced "value" */) {
resolve(value);
} else {
// Something went wrong because of "reason"
// The reason is traditionally an Error object, although
// this is not required or enforced.
let reason = new Error(message);
reject(reason);
// Throwing an error also rejects the promise.
throw reason;
}
});
Die then
und catch
Methoden können verwendet werden, um Callbacks für die Erfüllung und Zurückweisung anzuhängen:
promise.then(value => {
// Work has completed successfully,
// promise has been fulfilled with "value"
}).catch(reason => {
// Something went wrong,
// promise has been rejected with "reason"
});
Hinweis: Das Aufrufen von promise.then(...)
und promise.catch(...)
für dasselbe Versprechen kann zu einer Uncaught exception in Promise
promise.catch(...)
Uncaught exception in Promise
wenn ein Fehler auftritt, entweder während der Ausführung des Versprechens oder innerhalb eines der Rückrufe Die bevorzugte Methode wäre, den nächsten Listener mit dem Versprechen zu verknüpfen, das vom vorherigen then
/ catch
.
Alternativ können beide Rückrufe in einem einzigen Anruf verbunden werden, um then
:
promise.then(onFulfilled, onRejected);
Durch das Anhängen von Rückrufen an ein bereits abgeschlossenes Versprechen werden diese sofort in die Mikrotask-Warteschlange gestellt , und sie werden "so bald wie möglich" (dh unmittelbar nach dem aktuell ausgeführten Skript) aufgerufen. Im Gegensatz zu vielen anderen Implementierungen, die Ereignisse auslösen, ist es nicht erforderlich, den Status des Versprechens zu prüfen, bevor Callbacks angehängt werden.
Funktionsaufruf verzögern
Die setTimeout()
Methode ruft eine Funktion auf oder wertet einen Ausdruck nach einer angegebenen Anzahl von Millisekunden aus. Es ist auch ein trivialer Weg, um einen asynchronen Betrieb zu erreichen.
In diesem Beispiel löst der Aufruf der wait
Funktion das Versprechen nach der als erstes Argument angegebenen Zeit auf:
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
wait(5000).then(() => {
console.log('5 seconds have passed...');
});
Warten auf mehrere gleichzeitige Versprechen
Die statische Methode Promise.all()
akzeptiert ein iterierbares (z. B. ein Array
) Versprechen und gibt ein neues Versprechen zurück. Dieses wird aufgelöst, wenn alle Versprechungen im Iterierbaren aufgelöst wurden, oder lehnt ab, wenn mindestens eines der Versprechungen im Iterierbaren abgelehnt wurde.
// wait "millis" ms, then resolve with "value"
function resolve(value, milliseconds) {
return new Promise(resolve => setTimeout(() => resolve(value), milliseconds));
}
// wait "millis" ms, then reject with "reason"
function reject(reason, milliseconds) {
return new Promise((_, reject) => setTimeout(() => reject(reason), milliseconds));
}
Promise.all([
resolve(1, 5000),
resolve(2, 6000),
resolve(3, 7000)
]).then(values => console.log(values)); // outputs "[1, 2, 3]" after 7 seconds.
Promise.all([
resolve(1, 5000),
reject('Error!', 6000),
resolve(2, 7000)
]).then(values => console.log(values)) // does not output anything
.catch(reason => console.log(reason)); // outputs "Error!" after 6 seconds.
Nicht versprechende Werte im Iterierbaren werden "versprochen" .
Promise.all([
resolve(1, 5000),
resolve(2, 6000),
{ hello: 3 }
])
.then(values => console.log(values)); // outputs "[1, 2, { hello: 3 }]" after 6 seconds
Die Zerstörungszuordnung kann dabei helfen, Ergebnisse aus mehreren Zusagen abzurufen.
Promise.all([
resolve(1, 5000),
resolve(2, 6000),
resolve(3, 7000)
])
.then(([result1, result2, result3]) => {
console.log(result1);
console.log(result2);
console.log(result3);
});
Warten auf das erste von mehreren gleichzeitigen Versprechen
Die statische Methode Promise.race()
akzeptiert ein iterierbares Versprechen und gibt ein neues Versprechen zurück, das aufgelöst oder abgelehnt wird, sobald das erste Versprechen im iterierbaren Element gelöst oder abgelehnt wurde.
// wait "milliseconds" milliseconds, then resolve with "value"
function resolve(value, milliseconds) {
return new Promise(resolve => setTimeout(() => resolve(value), milliseconds));
}
// wait "milliseconds" milliseconds, then reject with "reason"
function reject(reason, milliseconds) {
return new Promise((_, reject) => setTimeout(() => reject(reason), milliseconds));
}
Promise.race([
resolve(1, 5000),
resolve(2, 3000),
resolve(3, 1000)
])
.then(value => console.log(value)); // outputs "3" after 1 second.
Promise.race([
reject(new Error('bad things!'), 1000),
resolve(2, 2000)
])
.then(value => console.log(value)) // does not output anything
.catch(error => console.log(error.message)); // outputs "bad things!" after 1 second
Werte "versprechen"
Die statische Promise.resolve
Methode kann verwendet werden, um Werte in Versprechungen zu verpacken.
let resolved = Promise.resolve(2);
resolved.then(value => {
// immediately invoked
// value === 2
});
Wenn value
bereits ein Versprechen ist, führt Promise.resolve
einfach neu ein.
let one = new Promise(resolve => setTimeout(() => resolve(2), 1000));
let two = Promise.resolve(one);
two.then(value => {
// 1 second has passed
// value === 2
});
In der Tat kann value
ein beliebiges "thenable" sein (Objekt, das eine then
Methode definiert, die hinreichend wie ein spezifikationskonformes Versprechen funktioniert). Auf diese Weise kann Promise.resolve
nicht vertrauenswürdige Objekte von Drittanbietern in vertrauenswürdige Versprechen von Promise.resolve
konvertieren.
let resolved = Promise.resolve({
then(onResolved) {
onResolved(2);
}
});
resolved.then(value => {
// immediately invoked
// value === 2
});
Die statische Methode Promise.reject
gibt ein Versprechen zurück, das sofort mit dem angegebenen reason
.
let rejected = Promise.reject("Oops!");
rejected.catch(reason => {
// immediately invoked
// reason === "Oops!"
});
"Promisifying" -Funktionen mit Callbacks
Bei einer Funktion, die einen Rückruf im Node-Stil akzeptiert,
fooFn(options, function callback(err, result) { ... });
Sie können es versprechen (konvertieren Sie es in eine auf Versprechen basierende Funktion) wie folgt:
function promiseFooFn(options) {
return new Promise((resolve, reject) =>
fooFn(options, (err, result) =>
// If there's an error, reject; otherwise resolve
err ? reject(err) : resolve(result)
)
);
}
Diese Funktion kann dann wie folgt verwendet werden:
promiseFooFn(options).then(result => {
// success!
}).catch(err => {
// error!
});
Allgemeiner gesagt, wie Sie eine bestimmte Callback-Style-Funktion versprechen:
function promisify(func) {
return function(...args) {
return new Promise((resolve, reject) => {
func(...args, (err, result) => err ? reject(err) : resolve(result));
});
}
}
Dies kann wie folgt verwendet werden:
const fs = require('fs');
const promisedStat = promisify(fs.stat.bind(fs));
promisedStat('/foo/bar')
.then(stat => console.log('STATE', stat))
.catch(err => console.log('ERROR', err));
Fehlerbehandlung
Fehler von Versprechen geworfen werden durch den zweiten Parameter behandelt ( reject
) zu übergeben , then
oder durch die Behandlungsroutine übergeben catch
:
throwErrorAsync()
.then(null, error => { /* handle error here */ });
// or
throwErrorAsync()
.catch(error => { /* handle error here */ });
Verkettung
Wenn Sie über eine Versprechungskette verfügen, wird ein Fehler dazu führen resolve
Lösungshandler übersprungen werden:
throwErrorAsync()
.then(() => { /* never called */ })
.catch(error => { /* handle error here */ });
Gleiches gilt für Ihre then
Funktionen. Wenn ein resolve
eine Ausnahme auslöst, wird der nächste reject
aufgerufen:
doSomethingAsync()
.then(result => { throwErrorSync(); })
.then(() => { /* never called */ })
.catch(error => { /* handle error from throwErrorSync() */ });
Ein Fehlerbehandlungsprogramm gibt ein neues Versprechen zurück, sodass Sie die Versprechenkette fortsetzen können. Das vom Fehlerhandler zurückgegebene Versprechen wird mit dem vom Handler zurückgegebenen Wert aufgelöst:
throwErrorAsync()
.catch(error => { /* handle error here */; return result; })
.then(result => { /* handle result here */ });
Sie können einen Fehler in einer Versprechungskette herunterfallen lassen, indem Sie den Fehler erneut auslösen:
throwErrorAsync()
.catch(error => {
/* handle error from throwErrorAsync() */
throw error;
})
.then(() => { /* will not be called if there's an error */ })
.catch(error => { /* will get called with the same error */ });
Es ist möglich, eine Ausnahme setTimeout
, die nicht durch das Versprechen behandelt wird, indem die throw
Anweisung in einen setTimeout
Callback eingeschlossen wird:
new Promise((resolve, reject) => {
setTimeout(() => { throw new Error(); });
});
Dies funktioniert, weil Versprechen nicht asynchron geworfene Ausnahmen behandeln können.
Unbehandelte Ablehnungen
Ein Fehler wird stillschweigend ignoriert, wenn ein Versprechen keinen catch
Blocker oder einen reject
Handler hat:
throwErrorAsync()
.then(() => { /* will not be called */ });
// error silently ignored
Um dies zu verhindern, verwenden Sie immer einen catch
Block:
throwErrorAsync()
.then(() => { /* will not be called */ })
.catch(error => { /* handle error*/ });
// or
throwErrorAsync()
.then(() => { /* will not be called */ }, error => { /* handle error*/ });
Alternativ abonnieren Sie das unhandledrejection
Ereignis alle unbehandelten abgelehnte Versprechen zu fangen:
window.addEventListener('unhandledrejection', event => {});
Einige Versprechen können ihre Ablehnung später als zu ihrer Erstellungszeit bewältigen. Das rejectionhandled
Ereignis wird ausgelöst, wenn ein solches Versprechen bearbeitet wird:
window.addEventListener('unhandledrejection', event => console.log('unhandled'));
window.addEventListener('rejectionhandled', event => console.log('handled'));
var p = Promise.reject('test');
setTimeout(() => p.catch(console.log), 1000);
// Will print 'unhandled', and after one second 'test' and 'handled'
Das event
enthält Informationen zur Ablehnung. event.reason
ist das event.promise
und event.promise
ist das Versprechungsobjekt, das das Ereignis verursacht hat.
In NodeJS die rejectionhandled
und unhandledrejection
Ereignisse genannt rejectionHandled
und unhandledRejection
auf process
ist, und haben eine andere Signatur:
process.on('rejectionHandled', (reason, promise) => {});
process.on('unhandledRejection', (reason, promise) => {});
Das Argument für den reason
ist das Fehlerobjekt, und das Argument für das promise
ist ein Verweis auf das Versprechungsobjekt, das das Ereignis ausgelöst hat.
Die Verwendung dieser nicht unhandledrejection
rejectionhandled
und rejectionhandled
sollte nur zu Debugging-Zwecken in Betracht gezogen werden. In der Regel sollten alle Versprechen ihre Ablehnungen behandeln.
Hinweis: Derzeit unterstützen nur Chrome 49+ und Node.js die nicht unhandledrejection
rejectionhandled
und die rejectionhandled
Ereignissen.
Vorsichtsmaßnahmen
Verketten mit fulfill
und reject
Die then(fulfill, reject)
(mit beiden Parametern nicht null
) hat ein eindeutiges und komplexes Verhalten und sollte nicht verwendet werden, wenn Sie nicht genau wissen, wie sie funktioniert.
Die Funktion funktioniert wie erwartet, wenn für eine der Eingaben null
angegeben wird:
// the following calls are equivalent
promise.then(fulfill, null)
promise.then(fulfill)
// the following calls are also equivalent
promise.then(null, reject)
promise.catch(reject)
Es nimmt jedoch ein eindeutiges Verhalten an, wenn beide Eingaben gegeben werden:
// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.then(fulfill).catch(reject)
// the following calls are not equivalent!
promise.then(fulfill, reject)
promise.catch(reject).then(fulfill)
Die then(fulfill, reject)
sieht aus, als wäre sie eine Abkürzung für then(fulfill).catch(reject)
, ist dies jedoch nicht und verursacht Probleme, wenn sie austauschbar verwendet wird. Ein solches Problem besteht darin, dass der reject
keine Fehler vom fulfill
. Folgendes wird passieren:
Promise.resolve() // previous promise is fulfilled
.then(() => { throw new Error(); }, // error in the fulfill handler
error => { /* this is not called! */ });
Der obige Code führt zu einem abgelehnten Versprechen, da der Fehler weitergegeben wird. Vergleichen Sie es mit dem folgenden Code, der zu einem erfüllten Versprechen führt:
Promise.resolve() // previous promise is fulfilled
.then(() => { throw new Error(); }) // error in the fulfill handler
.catch(error => { /* handle error */ });
Ein ähnliches Problem besteht, wenn then(fulfill, reject)
austauschbar mit catch(reject).then(fulfill)
mit erfüllt catch(reject).then(fulfill)
, zurückgewiesen catch(reject).then(fulfill)
, außer wenn erfüllte Versprechen propagiert und nicht abgelehnte Versprechen.
Synchron von einer Funktion werfen, die ein Versprechen zurückgeben sollte
Stellen Sie sich eine Funktion wie diese vor:
function foo(arg) {
if (arg === 'unexepectedValue') {
throw new Error('UnexpectedValue')
}
return new Promise(resolve =>
setTimeout(() => resolve(arg), 1000)
)
}
Wenn eine solche Funktion mitten in einer Versprechungskette verwendet wird, gibt es anscheinend kein Problem:
makeSomethingAsync().
.then(() => foo('unexpectedValue'))
.catch(err => console.log(err)) // <-- Error: UnexpectedValue will be caught here
Wenn jedoch dieselbe Funktion außerhalb einer Versprechungskette aufgerufen wird, wird der Fehler nicht von ihr gehandhabt und an die Anwendung ausgegeben:
foo('unexpectedValue') // <-- error will be thrown, so the application will crash
.then(makeSomethingAsync) // <-- will not run
.catch(err => console.log(err)) // <-- will not catch
Es gibt zwei mögliche Problemumgehungen:
Rückgabe eines abgelehnten Versprechens mit dem Fehler
Anstelle des Werfens gehen Sie wie folgt vor:
function foo(arg) {
if (arg === 'unexepectedValue') {
return Promise.reject(new Error('UnexpectedValue'))
}
return new Promise(resolve =>
setTimeout(() => resolve(arg), 1000)
)
}
Wickeln Sie Ihre Funktion in eine Versprechenkette
Ihre throw
wird ordnungsgemäß erfasst, wenn sie bereits in einer Versprechungskette enthalten ist:
function foo(arg) {
return Promise.resolve()
.then(() => {
if (arg === 'unexepectedValue') {
throw new Error('UnexpectedValue')
}
return new Promise(resolve =>
setTimeout(() => resolve(arg), 1000)
)
})
}
Synchronisierung von synchronen und asynchronen Vorgängen
In einigen Fällen möchten Sie möglicherweise eine synchrone Operation in ein Versprechen einschließen, um Wiederholungen in Code-Zweigen zu verhindern. Nehmen Sie dieses Beispiel:
if (result) { // if we already have a result
processResult(result); // process it
} else {
fetchResult().then(processResult);
}
Die synchronen und asynchronen Zweige des obigen Codes können abgeglichen werden, indem der Synchronbetrieb redundant in ein Versprechen eingebettet wird:
var fetch = result
? Promise.resolve(result)
: fetchResult();
fetch.then(processResult);
Wenn Sie das Ergebnis eines asynchronen Aufrufs zwischenspeichern, ist es vorzuziehen, das Versprechen anstelle des Ergebnisses selbst zu speichern. Dadurch wird sichergestellt, dass nur ein asynchroner Vorgang erforderlich ist, um mehrere parallele Anforderungen aufzulösen.
Es sollte darauf geachtet werden, dass zwischengespeicherte Werte ungültig werden, wenn Fehlerbedingungen auftreten.
// A resource that is not expected to change frequently
var planets = 'http://swapi.co/api/planets/';
// The cached promise, or null
var cachedPromise;
function fetchResult() {
if (!cachedPromise) {
cachedPromise = fetch(planets)
.catch(function (e) {
// Invalidate the current result to retry on the next fetch
cachedPromise = null;
// re-raise the error to propagate it to callers
throw e;
});
}
return cachedPromise;
}
Reduzieren Sie ein Array auf verkettete Versprechen
Dieses Entwurfsmuster ist nützlich, um aus einer Liste von Elementen eine Folge asynchroner Aktionen zu generieren.
Es gibt zwei Varianten:
- die "dann" -Reduktion, die eine Kette aufbaut, die solange andauert, wie die Kette Erfolg hat.
- die "Catch" -Reduktion, die eine Kette aufbaut, die solange andauert, wie die Kette Fehler aufweist.
Die "dann" Reduktion
Diese Variante des Musters bildet eine .then()
Kette und kann zum Verketten von Animationen oder zum .then()
einer Folge abhängiger HTTP-Anforderungen verwendet werden.
[1, 3, 5, 7, 9].reduce((seq, n) => {
return seq.then(() => {
console.log(n);
return new Promise(res => setTimeout(res, 1000));
});
}, Promise.resolve()).then(
() => console.log('done'),
(e) => console.log(e)
);
// will log 1, 3, 5, 7, 9, 'done' in 1s intervals
Erläuterung:
- Wir rufen
.reduce()
für ein.reduce()
auf und stellenPromise.resolve()
als AnfangswertPromise.resolve()
. - Jedes reduzierte Element fügt dem ursprünglichen Wert ein
.then()
. -
reduce()
‚s Produkt wird Promise.resolve (). dann (...). dann (...). - Nach dem Verkleinern fügen wir manuell einen
.then(successHandler, errorHandler)
, umsuccessHandler
auszuführen, sobald alle vorherigen Schritte gelöst wurden. Wenn ein Schritt fehlschlagen sollte, wirderrorHandler
ausgeführt.
Hinweis: Die "Dann" -Reduktion ist ein sequentielles Gegenstück von Promise.all()
.
Die "Fang" -Reduktion
Diese Variante des Musters baut eine .catch()
Kette auf und kann zum sequenziellen Suchen eines Satzes von Webservern nach gespiegelten Ressourcen verwendet werden, bis ein funktionierender Server gefunden wird.
var working_resource = 5; // one of the values from the source array
[1, 3, 5, 7, 9].reduce((seq, n) => {
return seq.catch(() => {
console.log(n);
if(n === working_resource) { // 5 is working
return new Promise((resolve, reject) => setTimeout(() => resolve(n), 1000));
} else { // all other values are not working
return new Promise((resolve, reject) => setTimeout(reject, 1000));
}
});
}, Promise.reject()).then(
(n) => console.log('success at: ' + n),
() => console.log('total failure')
);
// will log 1, 3, 5, 'success at 5' at 1s intervals
Erläuterung:
- Wir rufen
.reduce()
für ein.reduce()
auf und gebenPromise.reject()
als Anfangswert an. - Jedes reduzierte Element fügt dem ursprünglichen Wert ein
.catch()
. -
reduce()
‚s Produkt wirdPromise.reject().catch(...).catch(...)
.Promise.reject().catch(...).catch(...)
. - Wir fügen
.then(successHandler, errorHandler)
nach der Reduzierung manuell an, umsuccessHandler
auszuführen, sobald einer der vorherigen Schritte gelöst wurde. Wenn alle Schritte fehlschlagen würden, würdeerrorHandler
ausgeführt.
Hinweis: Die „fängt“ Reduktion ist ein sequentielles Gegenstück zu Promise.any()
(wie in realisiert bluebird.js
, aber derzeit nicht in nativer ECMAScript).
mit jedem Versprechen
Es ist möglich, eine Funktion ( cb
) effektiv anzuwenden, die ein Versprechen an jedes Element eines Arrays zurückgibt, wobei jedes Element auf die Verarbeitung wartet, bis das vorherige Element verarbeitet ist.
function promiseForEach(arr, cb) {
var i = 0;
var nextPromise = function () {
if (i >= arr.length) {
// Processing finished.
return;
}
// Process next function. Wrap in `Promise.resolve` in case
// the function does not return a promise
var newPromise = Promise.resolve(cb(arr[i], i));
i++;
// Chain to finish processing.
return newPromise.then(nextPromise);
};
// Kick off the chain.
return Promise.resolve().then(nextPromise);
};
Dies kann hilfreich sein, wenn Sie Tausende von Elementen nacheinander effizient bearbeiten müssen. Durch die Verwendung einer regulären for
Schleife zum Erstellen der Versprechen werden alle auf einmal erstellt und viel RAM beansprucht.
Bereinigung mit finally durchführen ()
Derzeit gibt es einen Vorschlag (noch nicht Teil des ECMAScript-Standards), einen finally
Rückruf zu den Versprechen hinzuzufügen, die unabhängig davon ausgeführt werden, ob das Versprechen erfüllt oder abgelehnt wird. Semantisch ähnelt dies der finally
Klausel des try
Blocks .
Normalerweise verwenden Sie diese Funktion für die Bereinigung:
var loadingData = true;
fetch('/data')
.then(result => processData(result.data))
.catch(error => console.error(error))
.finally(() => {
loadingData = false;
});
Es ist wichtig anzumerken, dass der finally
Rückruf den Status des Versprechens nicht beeinflusst. Es ist egal, welchen Wert es zurückgibt, das Versprechen bleibt im erfüllten / abgelehnten Zustand, den es zuvor hatte. Im obigen Beispiel wird das Versprechen mit dem Rückgabewert von processData(result.data)
, obwohl der finally
Rückruf undefined
.
Da der Standardisierungsprozess noch andauert, wird Ihre Implementierung von Versprechungen höchstwahrscheinlich keine finally
Callbacks unterstützen. Für synchrone Rückrufe können Sie diese Funktionalität jedoch mit einer Polyfill hinzufügen:
if (!Promise.prototype.finally) {
Promise.prototype.finally = function(callback) {
return this.then(result => {
callback();
return result;
}, error => {
callback();
throw error;
});
};
}
Asynchrone API-Anforderung
Dies ist ein Beispiel für einen einfachen GET
API-Aufruf, der die asynchrone Funktionalität nutzen soll.
var get = function(path) {
return new Promise(function(resolve, reject) {
let request = new XMLHttpRequest();
request.open('GET', path);
request.onload = resolve;
request.onerror = reject;
request.send();
});
};
Eine robustere Fehlerbehandlung kann mit den folgenden onload
und onerror
Funktionen ausgeführt werden.
request.onload = function() {
if (this.status >= 200 && this.status < 300) {
if(request.response) {
// Assuming a successful call returns JSON
resolve(JSON.parse(request.response));
} else {
resolve();
} else {
reject({
'status': this.status,
'message': request.statusText
});
}
};
request.onerror = function() {
reject({
'status': this.status,
'message': request.statusText
});
};
Verwenden von ES2017 async / await
Dasselbe Beispiel, Image Loading , kann mit asynchronen Funktionen geschrieben werden. Dies ermöglicht auch die Verwendung der üblichen try/catch
Methode für die Ausnahmebehandlung.
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.addEventListener('load', () => resolve(img));
img.addEventListener('error', () => {
reject(new Error(`Failed to load ${url}`));
});
img.src = url;
});
}
(async () => {
// load /image.png and append to #image-holder, otherwise throw error
try {
let img = await loadImage('http://example.com/image.png');
document.getElementById('image-holder').appendChild(img);
}
catch (error) {
console.error(error);
}
})();