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.
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.
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 są „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:
-
.reduce()
na tablicy źródłowej i dostarczamyPromise.resolve()
jako wartość początkową. - Każdy zredukowany element doda
.then()
do wartości początkowej. -
reduce()
„s produkt będzie Promise.resolve (). Następnie (...). Następnie (...). - 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ówczaserrorHandler
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:
-
.reduce()
na tablicy źródłowej i udostępniamyPromise.reject()
jako wartość początkową. - Każdy zredukowany element doda
.catch()
do wartości początkowej. - Produkt
reduce()
będziePromise.reject().catch(...).catch(...)
. - 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ówczaserrorHandler
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.
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);
}
})();