Jak czekać na wiele obietnic równolegle bez zachowania "Fail-fast"? [duplikat]

to pytanie ma już odpowiedzi tutaj : poczekaj, aż wszystkie obietnice się spełnią, nawet jeśli niektóre zostaną odrzucone (19 odpowiedzi) Zamknięte 2 lata temu.

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.

Czy istnieje trzecia droga? Gdzie egzekucja jest efektywnie równoległa, ale jedna porażka nie psuje całej paczki?
Author: Community, 2016-12-22

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 .

 69
Author: Ben Aston,
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.

 112
Author: NoNameProvided,
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