Szukaj…


Składnia

  • nowa obietnica (/ * funkcja executora: * / function (rozwiń, odrzuć) {})
  • promise.then (onFulfilled [, onRejected])
  • promise.catch (onRejected)
  • Promise.resolve (rozdzielczość)
  • Promise.reject (powód)
  • Promise.all (iterowalne)
  • Promise.race (iterable)

Uwagi

Obietnice są częścią specyfikacji ECMAScript 2015, a obsługa przeglądarek jest ograniczona, a 88% przeglądarek na całym świecie obsługuje ją od lipca 2017 r. Poniższa tabela zawiera przegląd najwcześniejszych wersji przeglądarek obsługujących obietnice.

Chrom Krawędź Firefox Internet Explorer Opera Opera Mini Safari iOS Safari
32 12 27 x 19 x 7.1 8

W środowiskach, które ich nie obsługują, Promise może być wypełniana. Biblioteki stron trzecich mogą również zapewniać rozszerzone funkcje, takie jak automatyczne „promisyfikacja” funkcji oddzwaniania lub dodatkowe metody, takie jak progress - znany również jako notify .

Standardowa witryna internetowa Promises / A + zawiera listę implementacji zgodnych z wersjami 1.0 i 1.1 . Wywołania zwrotne obietnic oparte na standardzie A + są zawsze wykonywane asynchronicznie jako mikroprocesory w pętli zdarzeń .

Obiecuj łańcuchy

then metoda obietnicy zwraca nową obietnicę.

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

Zwrócenie Promise z oddzwaniania then doda ją do łańcucha obietnic.

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

catch pozwala odrzucić obietnica odzyskać, w sposób podobny do catch w ciągu try / catch prac oświadczenie. Każdy łańcuch, który then po catch wykona swoją procedurę rozwiązywania przy użyciu wartości rozstrzygniętej z catch .

const p = new Promise(resolve => {throw 'oh no'});
p.catch(() => 'oh yes').then(console.log.bind(console));  // outputs "oh yes"

Jeśli w środku łańcucha nie ma żadnych programów obsługi catch lub reject , catch na końcu przechwytuje wszelkie odrzucenia w łańcuchu:

p.catch(() => Promise.reject('oh yes'))
  .then(console.log.bind(console))      // won't be called
  .catch(console.error.bind(console));  // outputs "oh yes"

W niektórych przypadkach możesz chcieć „rozgałęzić” wykonanie funkcji. Możesz to zrobić, zwracając różne obietnice z funkcji w zależności od warunków. Później w kodzie możesz połączyć wszystkie te gałęzie w jedną, aby wywołać inne funkcje na nich i / lub obsłużyć wszystkie błędy w jednym miejscu.

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

Tak więc kolejność wykonywania funkcji wygląda następująco:

promise --> handlerFn1 -> handlerFn2 --> handlerFn5 ~~> .catch()
         |                            ^
         V                            |
         -> handlerFn3 -> handlerFn4 -^            

Pojedynczy catch otrzyma błąd w dowolnej gałęzi, w której może wystąpić.

Wprowadzenie

Obiekt Promise reprezentuje operację, która wygenerowała lub ostatecznie wygeneruje wartość. Obietnice zapewniają niezawodny sposób na zawinięcie (prawdopodobnie w toku) wyniku pracy asynchronicznej, łagodząc problem głęboko zagnieżdżonych wywołań zwrotnych (znanych jako „ piekło wywołania zwrotnego ”).

Przepływy stanów i kontroli

Obietnica może być w jednym z trzech stanów:

  • w toku - operacja bazowa jeszcze się nie zakończyła, a obietnica oczekuje na spełnienie.
  • spełnione - Operacja zakończyła się, a obietnica została spełniona z wartością . Jest to analogiczne do zwracania wartości z funkcji synchronicznej.
  • odrzucone - wystąpił błąd podczas operacji, a obietnica została odrzucona z podanego powodu . Jest to analogiczne do zgłaszania błędu w funkcji synchronicznej.

Mówi się, że obietnica zostanie rozliczona (lub rozwiązana ), gdy zostanie spełniona lub odrzucona. Po spełnieniu obietnicy staje się ona niezmienna i jej stan nie może się zmienić. Te then i catch metody obietnicy może być używana do mocowania callbacków wykonujące kiedy jest uregulowane. Te wywołania zwrotne są wywoływane odpowiednio z wartością spełnienia i powodem odrzucenia.

Diagram przepływu obietnicy

Przykład

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

Do załączenia wywołań zwrotnych spełnienia i odrzucenia można użyć metod then i catch :

promise.then(value => {
    // Work has completed successfully,
    // promise has been fulfilled with "value"
}).catch(reason => {
    // Something went wrong,
    // promise has been rejected with "reason"
});

Uwaga: Wywołanie promise.then(...) i promise.catch(...) dla tej samej obietnicy może spowodować Uncaught exception in Promise jeśli wystąpi błąd, zarówno podczas wykonywania obietnicy, jak i wewnątrz jednego z wywołań zwrotnych, więc preferowanym sposobem byłoby dołączenie następnego słuchacza do obietnicy zwróconej przez poprzednie then / catch .

Alternatywnie oba połączenia zwrotne można połączyć w jedno połączenie, aby then :

promise.then(onFulfilled, onRejected);

Dołączenie wywołań zwrotnych do obietnicy, która została już rozliczona, spowoduje natychmiastowe umieszczenie ich w kolejce do wykonywania mikroprocesorów i wywołanie ich „jak najszybciej” (tj. Natychmiast po aktualnie wykonywanym skrypcie). Nie jest konieczne sprawdzanie stanu obietnicy przed dołączeniem wywołań zwrotnych, w przeciwieństwie do wielu innych implementacji emitujących zdarzenia.


Demo na żywo

Opóźnienie wywołania funkcji

Metoda setTimeout() wywołuje funkcję lub ocenia wyrażenie po określonej liczbie milisekund. Jest to również trywialny sposób na osiągnięcie operacji asynchronicznej.

W tym przykładzie wywołanie funkcji wait rozwiązuje obietnicę po czasie określonym jako pierwszy argument:

function wait(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));  
}

wait(5000).then(() => { 
    console.log('5 seconds have passed...');
});

Oczekiwanie na wiele jednoczesnych obietnic

Metoda statyczna Promise.all() przyjmuje iterowalną (np. Array ) obietnic i zwraca nową obietnicę, która jest rozpatrywana, gdy wszystkie obietnice w iteracji zostały rozwiązane, lub odrzuca, jeśli przynajmniej jedna z obietnic w iteracji została odrzucona.

// 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.

Wartości niebędące przyrzeczeniem w iterowalnych„obiecane” .

Promise.all([
    resolve(1, 5000),
    resolve(2, 6000),
    { hello: 3 }
])
.then(values => console.log(values)); // outputs "[1, 2, { hello: 3 }]" after 6 seconds

Przydział destrukcji może pomóc w uzyskaniu wyników z wielu obietnic.

Promise.all([
    resolve(1, 5000),
    resolve(2, 6000),
    resolve(3, 7000)
])
.then(([result1, result2, result3]) => {
    console.log(result1);
    console.log(result2);
    console.log(result3);
});

Oczekiwanie na pierwszą z wielu jednoczesnych obietnic

Metoda statyczna Promise.race() przyjmuje iterowalną obietnicę i zwraca nową obietnicę, która jest rozpatrywana lub odrzucana, gdy tylko pierwsza z obietnic w iteracji zostanie rozwiązana lub odrzucona.

// 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

Wartości „obiecujące”

Można Promise.resolve metodę statyczną Promise.resolve do zawijania wartości w obietnicach.

let resolved = Promise.resolve(2);
resolved.then(value => {
    // immediately invoked
    // value === 2
});

Jeśli value jest już obietnicą, Promise.resolve po prostu ją przekształca.

let one = new Promise(resolve => setTimeout(() => resolve(2), 1000));
let two = Promise.resolve(one);
two.then(value => {
    // 1 second has passed
    // value === 2
});

W rzeczywistości value może być dowolny „niemożliwy” (obiekt definiujący metodę then , która działa wystarczająco podobnie do obietnicy zgodnej ze specyfikacją). Dzięki temu Promise.resolve może przekształcić niezaufane obiekty firm zewnętrznych w zaufane obietnice firm zewnętrznych.

let resolved = Promise.resolve({
    then(onResolved) {
        onResolved(2);
    }
});
resolved.then(value => {
    // immediately invoked
    // value === 2
});

Promise.reject statyczna Promise.reject zwraca obietnicę, która natychmiast odrzuca podany reason .

let rejected = Promise.reject("Oops!");
rejected.catch(reason => {
    // immediately invoked
    // reason === "Oops!"
});

Funkcje „obiecujące” z wywołaniami zwrotnymi

Biorąc pod uwagę funkcję, która akceptuje wywołanie zwrotne w stylu węzła,

fooFn(options, function callback(err, result) { ... });

możesz to obiecać (przekonwertować na funkcję opartą na obietnicach) w następujący sposób:

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

Tej funkcji można następnie użyć w następujący sposób:

promiseFooFn(options).then(result => {
    // success!
}).catch(err => {
    // error!
});

W bardziej ogólny sposób, oto jak obiecać dowolną funkcję w stylu wywołania zwrotnego:

function promisify(func) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            func(...args, (err, result) => err ? reject(err) : resolve(result));
        });
    }
}

Można to wykorzystać w następujący sposób:

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

Obsługa błędów

Błędy zgłaszane przez obietnice są obsługiwane przez drugi parametr ( reject ) przekazany do then momentu lub przez moduł obsługi przekazany do catch :

throwErrorAsync()
  .then(null, error => { /* handle error here */ });
// or
throwErrorAsync()
  .catch(error => { /* handle error here */ });

Łańcuch

Jeśli masz łańcuch obietnic, błąd spowoduje pominięcie procedur obsługi resolve :

throwErrorAsync()
  .then(() => { /* never called */ })
  .catch(error => { /* handle error here */ });

To samo dotyczy twoich then funkcji. Jeśli program obsługi resolve zgłasza wyjątek, zostanie wywołana kolejna procedura obsługi reject :

doSomethingAsync()
  .then(result => { throwErrorSync(); })
  .then(() => { /* never called */ })
  .catch(error => { /* handle error from throwErrorSync() */ });

Procedura obsługi błędów zwraca nową obietnicę, umożliwiając kontynuację łańcucha obietnic. Obietnica zwrócona przez moduł obsługi błędów jest rozwiązywana za pomocą wartości zwróconej przez moduł obsługi:

throwErrorAsync()
  .catch(error => { /* handle error here */; return result; })
  .then(result => { /* handle result here */ });

Możesz pozwolić, aby błąd spłynął kaskadowo do łańcucha obietnic, ponownie rzucając błąd:

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

Możliwe jest zgłoszenie wyjątku, który nie jest obsługiwany przez obietnicę, poprzez zawinięcie instrukcji throw w wywołaniu zwrotnym setTimeout :

new Promise((resolve, reject) => {
  setTimeout(() => { throw new Error(); });
});

Działa to, ponieważ obietnice nie są w stanie obsłużyć wyjątków zgłoszonych asynchronicznie.

Nieobsługiwane odrzucenia

Błąd zostanie po cichu zignorowany, jeśli obietnica nie ma bloku catch lub programu obsługi reject :

throwErrorAsync()
  .then(() => { /* will not be called */ });
// error silently ignored

Aby temu zapobiec, zawsze używaj bloku catch :

throwErrorAsync()
  .then(() => { /* will not be called */ })
  .catch(error => { /* handle error*/ });
// or
throwErrorAsync()
  .then(() => { /* will not be called */ }, error => { /* handle error*/ });

Alternatywnie, zapisz się na unhandledrejection razie złapać wszystkie nieobsługiwane odrzuconych obietnice:

window.addEventListener('unhandledrejection', event => {});

Niektóre obietnice mogą poradzić sobie z ich odrzuceniem później niż z czasem stworzenia. Zdarzenie obsługiwane przez rejectionhandled jest uruchamiane za każdym razem, gdy zostanie spełniona taka obietnica:

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'

Argument event zawiera informacje o odrzuceniu. event.reason to obiekt błędu, a event.promise to obiekt obietnicy, który spowodował zdarzenie.

W Nodejs rejectionhandled i unhandledrejection zdarzenia nazywane są rejectionHandled i unhandledRejection na process , odpowiednio, i mieć inny podpis:

process.on('rejectionHandled', (reason, promise) => {});
process.on('unhandledRejection', (reason, promise) => {});

Argumentem reason jest obiekt błędu, a argumentem promise jest odwołanie do obiektu przyrzeczenia, który spowodował uruchomienie zdarzenia.

Wykorzystanie tych unhandledrejection zdarzeń rejectionhandled i rejectionhandled należy rozpatrywać wyłącznie do celów debugowania. Zazwyczaj wszystkie obietnice powinny obsługiwać ich odrzucenia.

Uwaga: obecnie tylko Chrome 49+ i Node.js obsługują unhandledrejection zdarzenia rejectionhandled i rejectionhandled .

Ostrzeżenia

Łączenie z fulfill i reject

Funkcja then(fulfill, reject) (z obydwoma parametrami null ) ma unikalne i złożone zachowanie i nie należy jej używać, chyba że wiesz dokładnie, jak to działa.

Funkcja działa zgodnie z oczekiwaniami, jeśli ma null dla jednego z danych wejściowych:

// 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)

Przyjmuje jednak unikalne zachowanie, gdy podane są oba dane wejściowe:

// 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)

Funkcja then(fulfill, reject) wygląda jak skrót do then(fulfill).catch(reject) , ale tak nie jest i spowoduje problemy, jeśli będzie używana zamiennie. Jednym z takich problemów jest to, że reject obsługi reject nie obsługuje błędów z modułu obsługi fulfill . Oto co się stanie:

Promise.resolve() // previous promise is fulfilled
    .then(() => { throw new Error(); }, // error in the fulfill handler
        error => { /* this is not called! */ });

Powyższy kod spowoduje odrzucenie obietnicy, ponieważ błąd jest propagowany. Porównaj go z następującym kodem, co daje spełnioną obietnicę:

Promise.resolve() // previous promise is fulfilled
    .then(() => { throw new Error(); }) // error in the fulfill handler
    .catch(error => { /* handle error */ });

Podobny problem istnieje, gdy używa się then(fulfill, reject) zamiennie z catch(reject).then(fulfill) , z wyjątkiem propagowania spełnionych obietnic zamiast odrzuconych obietnic.

Synchroniczne rzucanie z funkcji, która powinna zwrócić obietnicę

Wyobraź sobie taką funkcję:

function foo(arg) {
  if (arg === 'unexepectedValue') {
    throw new Error('UnexpectedValue')
  }

  return new Promise(resolve => 
    setTimeout(() => resolve(arg), 1000)
  )
}

Jeśli taka funkcja jest używana w środku łańcucha obietnic, to najwyraźniej nie ma problemu:

makeSomethingAsync().
  .then(() => foo('unexpectedValue'))
  .catch(err => console.log(err)) // <-- Error: UnexpectedValue will be caught here

Jeśli jednak ta sama funkcja zostanie wywołana poza łańcuchem obietnicy, błąd nie zostanie przez nią obsłużony i zostanie zgłoszony do aplikacji:

foo('unexpectedValue') // <-- error will be thrown, so the application will crash
  .then(makeSomethingAsync) // <-- will not run
  .catch(err => console.log(err)) // <-- will not catch

Istnieją 2 możliwe obejścia:

Zwróć odrzuconą obietnicę z błędem

Zamiast rzucać, wykonaj następujące czynności:

function foo(arg) {
  if (arg === 'unexepectedValue') {
    return Promise.reject(new Error('UnexpectedValue'))
  }

  return new Promise(resolve => 
    setTimeout(() => resolve(arg), 1000)
  )
}

Zawiń swoją funkcję w łańcuch obietnic

Twoje oświadczenie o throw zostanie poprawnie przechwycone, gdy będzie już w łańcuchu obietnic:

function foo(arg) {
  return Promise.resolve()
    .then(() => {
      if (arg === 'unexepectedValue') {
        throw new Error('UnexpectedValue')
      }

      return new Promise(resolve => 
        setTimeout(() => resolve(arg), 1000)
      )
    })
}

Uzgadnianie operacji synchronicznych i asynchronicznych

W niektórych przypadkach możesz chcieć zawinąć operację synchroniczną w obietnicy, aby zapobiec powtórzeniu się w gałęziach kodu. Weź ten przykład:

if (result) { // if we already have a result
  processResult(result); // process it
} else {
  fetchResult().then(processResult);
}

Synchroniczne i asynchroniczne gałęzie powyższego kodu można pogodzić przez nadmiarowe zawijanie operacji synchronicznej w obietnicy:

var fetch = result
  ? Promise.resolve(result)
  : fetchResult();

fetch.then(processResult);

Podczas buforowania wyniku asynchronicznego wywołania lepiej jest buforować obietnicę niż sam wynik. Zapewnia to, że tylko jedna operacja asynchroniczna jest wymagana do rozwiązania wielu równoległych żądań.

Należy zachować ostrożność, aby unieważnić buforowane wartości w przypadku wystąpienia warunków błędu.

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

Ogranicz tablicę do przyklejonych obietnic

Ten wzorzec projektowy jest użyteczny do generowania sekwencji działań asynchronicznych z listy elementów.

Istnieją dwa warianty:

  • redukcja „wtedy”, która tworzy łańcuch, który trwa tak długo, jak długo łańcuch odniesie sukces.
  • redukcja „catch”, która buduje łańcuch, który trwa tak długo, jak długo łańcuch wystąpi błąd.

Redukcja „wtedy”

Ten wariant wzorca buduje łańcuch .then() i może być używany do łączenia animacji lub tworzenia sekwencji zależnych żądań HTTP.

[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

Wyjaśnienie:

  1. .reduce() na tablicy źródłowej i dostarczamy Promise.resolve() jako wartość początkową.
  2. Każdy zredukowany element doda .then() do wartości początkowej.
  3. reduce() „s produkt będzie Promise.resolve (). Następnie (...). Następnie (...).
  4. Po zmniejszeniu ręcznie .then(successHandler, errorHandler) , aby wykonać successHandler po rozwiązaniu wszystkich poprzednich kroków. Jeśli jakikolwiek krok miałby się nie powieść, wówczas errorHandler wykonałby się.

Uwaga: Redukcja „następnie” jest sekwencyjnym odpowiednikiem Promise.all() .

Redukcja „połowu”

Ten wariant wzorca buduje łańcuch .catch() i może być używany do sekwencyjnego sondowania zestawu serwerów WWW dla niektórych zasobów lustrzanych, dopóki nie zostanie znaleziony działający serwer.

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

Wyjaśnienie:

  1. .reduce() na tablicy źródłowej i udostępniamy Promise.reject() jako wartość początkową.
  2. Każdy zredukowany element doda .catch() do wartości początkowej.
  3. Produkt reduce() będzie Promise.reject().catch(...).catch(...) .
  4. Po .then(successHandler, errorHandler) ręcznie dołączamy .then(successHandler, errorHandler) , aby wykonać successHandler po rozwiązaniu któregokolwiek z poprzednich kroków. Jeśli wszystkie kroki nie powiodą się, wówczas errorHandler wykona się.

Uwaga: Redukcja „catch” jest sekwencyjnym odpowiednikiem Promise.any() (zaimplementowanym w bluebird.js , ale obecnie nie w natywnym ECMAScript).

Każdy z obietnicami

Możliwe jest skuteczne zastosowanie funkcji ( cb ), która zwraca obietnicę do każdego elementu tablicy, przy czym każdy element czeka na przetworzenie do przetworzenia poprzedniego elementu.

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

Może to być pomocne, jeśli chcesz wydajnie przetwarzać tysiące przedmiotów, pojedynczo. Korzystanie ze zwykłej pętli for do tworzenia obietnic utworzy je wszystkie jednocześnie i zajmie znaczną ilość pamięci RAM.

Przeprowadzanie czyszczenia za pomocą w końcu ()

Obecnie istnieje propozycja (jeszcze nie będąca częścią standardu ECMAScript), aby w finally dodać wywołanie zwrotne do obietnic, które zostaną wykonane niezależnie od tego, czy obietnica zostanie spełniona, czy odrzucona. Semantycznie jest to podobne do finally klauzuli bloku try .

Zwykle używasz tej funkcji do czyszczenia:

var loadingData = true;

fetch('/data')
    .then(result => processData(result.data))
    .catch(error => console.error(error))
    .finally(() => {
        loadingData = false;
    });

Należy zauważyć, że finally wywołanie zwrotne nie wpływa na stan obietnicy. Nie ma znaczenia, jaką wartość zwraca, obietnica pozostaje w stanie spełnionym / odrzuconym, który miała wcześniej. Tak więc w powyższym przykładzie obietnica zostanie rozwiązana za pomocą wartości zwracanej przez processData(result.data) nawet jeśli finally wywołanie zwrotne zwróciło się undefined .

Z procesem standaryzacji nadal jest w toku, implementacja obietnice najprawdopodobniej nie wesprze finally wywołania zwrotne po wyjęciu z pudełka. W przypadku synchronicznych wywołań zwrotnych możesz dodać tę funkcjonalność do funkcji wypełniania polifill:

if (!Promise.prototype.finally) {
    Promise.prototype.finally = function(callback) {
        return this.then(result => {
            callback();
            return result;
        }, error => {
            callback();
            throw error;
        });
    };
}

Żądanie asynchronicznego interfejsu API

Jest to przykład prostego wywołania API GET zawiniętego w obietnicę skorzystania z jego asynchronicznej funkcjonalności.

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

Bardziej niezawodną obsługę błędów można wykonać za pomocą następujących funkcji onload i onerror .

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

Korzystanie z asynchronizacji / oczekiwania na ES2017

Ten sam przykład powyżej, Ładowanie obrazu , można zapisać za pomocą funkcji asynchronicznych . Pozwala to również na użycie wspólnej metody try/catch do obsługi wyjątków.

Uwaga: od kwietnia 2017 r. Obecne wersje wszystkich przeglądarek, ale Internet Explorer obsługuje funkcje asynchroniczne .

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow