113 votes

Comment puis-je attendre plusieurs promesses en parallèle sans comportement «rapide»?

Je suis à l'aide d' async/await de tirer plusieurs api des appels en parallèle:

async function foo(arr) {
  const results = await Promise.all(arr.map(v => {
     return doAsyncThing(v)
  }))
  return results
}

Je sais que, contrairement à l' loops, Promise.all s'exécute en parallèle (qui est, le temps d'attente pour les résultats, partie est en parallèle).

Mais je sais aussi que:

Promesse.tout est rejetée si l'un des éléments est rejeté et Promesse.tous échoue rapide: Si vous avez quatre promesses qui résoudre après un délai d'attente, et on rejette immédiatement, alors la Promesse.tous les rejette immédiatement.

Comme je l'ai lu cela, si j' Promise.all avec 5 promesses, et le premier à terminer renvoie une reject(), alors que les 4 autres sont effectivement annulées et leur a promis resolve() valeurs sont perdues.

Est-il une troisième voie? Où l'exécution est effectivement en parallèle, mais une défaillance unique ne pas gâcher le tas?

124voto

NoNameProvided Points 234

Alors que la technique de la accepté de répondre à résoudre votre problème, c'est l'anti-modèle. La résolution de la promesse, une erreur n'est pas une bonne pratique et il y a une manière plus propre de le faire.

Ce que vous voulez faire est en pseudo langage est:

fn task() {
  result-1 = doAsync();
  result-n = doAsync();

  // handle results together
  return handleResults(result-1, ..., result-n)
}

Ceci peut être réalisé simplement avec de l' async/await sans avoir besoin de l'aide d' Promise.all. Un exemple:

console.clear();

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

/** 
 * This will be runned in series, because 
 * we call a function and immediately wait for it's 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');

Remarque: Vous aurez besoin d'un navigateur qui a async/await activé pour exécuter ce code.

De cette façon, vous pouvez utiliser simplement try/ catch gérer vos erreurs, et de renvoyer des résultats partiels à l'intérieur d' parallel fonction.

83voto

Ben Points 6629

À l'aide de catch signifie que la promesse est résolu (sauf si vous jetez une exception à la règle de l' catch ou manuellement rejeter la promesse de la chaîne), de sorte que vous n'avez pas besoin de renvoyer explicitement résolue promesse IIUC.

Cela signifie que, tout simplement, par des erreurs de manipulation avec catch vous pouvez obtenir ce que vous voulez.

Si vous voulez normaliser la façon dont les rejets sont traités ensuite, vous pouvez appliquer un rejet pour le traitement de la fonction de toutes les promesses.

async function bar() {
    await new Promise(r=> setTimeout(r, 1000))
      .then(()=> console.log('bar'))
      .then(()=> 'bar result');
}
async function bam() {
    await new Promise((ignore, reject)=> setTimeout(reject, 2000))
      .catch(()=> { console.log('bam errored'); throw 'bam'; });
}
async function bat() {
    await new Promise(r=> setTimeout(r, 3000))
      .then(()=> console.log('bat'))
      .then(()=> 'bat result');
}

function handleRejection(p) {
    return p.catch(err=> ({ error: err }));
}

async function foo(arr) {
  console.log('foo');
  return await Promise.all([bar(), bam(), bat()].map(handleRejection));
}

foo().then(results=> console.log('done', results));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X