Jak czekać na wiele obietnic równolegle bez zachowania "Fail-fast"? [duplikat]
Używam async
/await
aby odpalić kilka api
wywołań równolegle:
async function foo(arr) {
const results = await Promise.all(arr.map(v => {
return doAsyncThing(v)
}))
return results
}
Wiem, że w przeciwieństwie do loops
, Promise.all
wykonuje w-parallel (tzn. część oczekująca na wyniki jest równoległa).
Ale wiem też, że :
Obiecuję.wszystkie zostaną odrzucone, jeżeli jeden z elementów zostanie odrzucony i Obiecuję.wszystko szybko zawodzi: jeśli masz cztery obietnice, które rozwiązują po a timeout, i odrzuca się natychmiast, a potem obiecuje.all rejects natychmiast.
Jak to czytam, Jeśli Promise.all
z 5 obietnicami, a pierwszy, który zakończy, zwróci reject()
, to pozostałe 4 zostaną skutecznie anulowane, a ich obiecane wartości resolve()
zostaną utracone.
2 answers
ES2020 zawiera obietnicę.allSettled , który zrobi to, co chcesz.
Promise.allSettled([
Promise.resolve('a'),
Promise.reject('b')
]).then(console.log)
Wyjście:
[
{
"status": "fulfilled",
"value": "a"
},
{
"status": "rejected",
"reason": "b"
}
]
Ale jeśli chcesz "wywalić własne", możesz wykorzystać fakt, że za pomocą Promise#catch
oznacza to, że obietnica rozwiązuje (chyba że wyrzucisz wyjątek z catch
lub ręcznie odrzucisz łańcuch obietnic), więc nie musisz zwracać rozwiązanej obietnicy.
Więc, po prostu obsługując błędy za pomocą catch
, możesz osiągnąć to, co chcesz.
Zauważ, że jeśli chcesz, aby błędy były widoczne w wyniku, musisz zdecydować się na konwencję ich wynurzenia.
Możesz zastosować funkcję obsługi odrzuceń do każdej obietnicy w zbiorze, używając Array # map i użyj Promise./ align = "left" /
Przykład
Należy wydrukować:
Elapsed Time Output
0 started...
1s foo completed
1s bar completed
2s bam errored
2s done [
"foo result",
"bar result",
{
"error": "bam"
}
]
async function foo() {
await new Promise((r)=>setTimeout(r,1000))
console.log('foo completed')
return 'foo result'
}
async function bar() {
await new Promise((r)=>setTimeout(r,1000))
console.log('bar completed')
return 'bar result'
}
async function bam() {
try {
await new Promise((_,reject)=>setTimeout(reject,2000))
} catch {
console.log('bam errored')
throw 'bam'
}
}
function handleRejection(p) {
return p.catch((error)=>({
error
}))
}
function waitForAll(...ps) {
console.log('started...')
return Promise.all(ps.map(handleRejection))
}
waitForAll(foo(), bar(), bam()).then(results=>console.log('done', results))
Zobacz także .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-02-27 13:50:43
Podczas gdy technika w zaakceptowanej odpowiedzi może rozwiązać twój problem, jest to anty-wzorzec. Rozwiązywanie obietnicy z błędem nie jest dobrą praktyką i istnieje czystszy sposób na to.
To, co chcesz zrobić, w pseudo-kodzie, to:
fn task() {
result-1 = doAsync();
result-n = doAsync();
// handle results together
return handleResults(result-1, ..., result-n)
}
Można to osiągnąć po prostu za pomocą async
/await
bez konieczności używania Promise.all
. Przykład roboczy:
console.clear();
function wait(ms, data) {
return new Promise( resolve => setTimeout(resolve.bind(this, data), ms) );
}
/**
* These will be run in series, because we call
* a function and immediately wait for each result,
* so this will finish in 1s.
*/
async function series() {
return {
result1: await wait(500, 'seriesTask1'),
result2: await wait(500, 'seriesTask2'),
}
}
/**
* While here we call the functions first,
* then wait for the result later, so
* this will finish in 500ms.
*/
async function parallel() {
const task1 = wait(500, 'parallelTask1');
const task2 = wait(500, 'parallelTask2');
return {
result1: await task1,
result2: await task2,
}
}
async function taskRunner(fn, label) {
const startTime = performance.now();
console.log(`Task ${label} starting...`);
let result = await fn();
console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}
void taskRunner(series, 'series');
void taskRunner(parallel, 'parallel');
Uwaga: potrzebna będzie przeglądarka, która ma async
/await
enabled to run this snippet.
W ten sposób możesz użyć po prostu try
/ catch
do obsługi błędów i zwracania częściowych wyników wewnątrz funkcji parallel
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-17 23:25:34