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.

Versprechen Flussdiagramm

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.


Live-Demo

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:

  1. Wir rufen .reduce() für ein .reduce() auf und stellen Promise.resolve() als Anfangswert Promise.resolve() .
  2. Jedes reduzierte Element fügt dem ursprünglichen Wert ein .then() .
  3. reduce() ‚s Produkt wird Promise.resolve (). dann (...). dann (...).
  4. Nach dem Verkleinern fügen wir manuell einen .then(successHandler, errorHandler) , um successHandler auszuführen, sobald alle vorherigen Schritte gelöst wurden. Wenn ein Schritt fehlschlagen sollte, wird errorHandler 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:

  1. Wir rufen .reduce() für ein .reduce() auf und geben Promise.reject() als Anfangswert an.
  2. Jedes reduzierte Element fügt dem ursprünglichen Wert ein .catch() .
  3. reduce() ‚s Produkt wird Promise.reject().catch(...).catch(...) . Promise.reject().catch(...).catch(...) .
  4. Wir fügen .then(successHandler, errorHandler) nach der Reduzierung manuell an, um successHandler auszuführen, sobald einer der vorherigen Schritte gelöst wurde. Wenn alle Schritte fehlschlagen würden, würde errorHandler 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.

Hinweis: Ab April 2017 unterstützen die aktuellen Versionen aller Browser außer Internet Explorer asynchrone Funktionen .

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);
    }

})();


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow