76 votes

Comment exécuter async / wait en parallèle en Javascript

Enfin async/await seront pris en charge dans tous les principaux navigateurs bientôt sauf IE. Ainsi, nous pouvons maintenant commencer à écrire un code plus lisible avec async/await mais il y a un hic. Beaucoup de gens utilisent async attendent comme ceci:

const userResponse = await fetchUserAsync();
const postsResponse = await fetchPostsAsync();

Bien que ce code soit lisible, il a un problème, il exécute les fonctions de la série, de ne pas démarrer l'extraction de messages jusqu'à ce que l'extraction de l'utilisateur est terminé. La solutions est simple, nous avons besoin de récupérer les ressources en parallèle.

Donc, ce que je veux faire, c'est (en pseudo-langage):

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

  // handle results together
  combinedResult = handleResults(result-1, result-2);

  lastResult = handleLastResult(result-n);
}

153voto

NoNameProvided Points 234

Vous pouvez écrire quelque chose comme ceci:

const responses = await Promise.all([
 fetchUserAsync(),
 fetchPostsAsync(),
]);

const userResponse = responses[0];
const postsResponse = responses[1];

C'est facile à droite? Mais il y a un hic. Promise.all a fail-fast comportement qui signifie, qu'elle rejette dès que l'une des promesses rejeté. Probablement vous voulez une solution plus robuste où nous sommes en charge de la gestion des rejets tout de l'extrait. Heureusement il existe une solution, il 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 run 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');


/* 
 * The result will be:
 * Task series starting...
 * Task parallel starting...
 * Task parallel finished in 500 milliseconds with, { "result1": "parallelTask1", "result2": "parallelTask2" }
 * Task series finished in 1001 milliseconds with, { "result1": "seriesTask1", "result2": "seriesTask2" }
 */

Remarque: Vous aurez besoin d'un navigateur qui a async/await activé pour exécuter cet extrait de code (ou nodejs v7 et ci-dessus)

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

25voto

ricka Points 133

Si vous êtes d'accord avec le comportement d'échec rapide de Promise.all et la syntaxe d'attribution de déstructuration:

 const [userResponse, postsResponse] = await Promise.all([
  fetchUserAsync(),
  fetchPostsAsync(),
]);
 

5voto

Wilco Points 91

Pour ceux qui demandent comment étendre cela à un nombre d'appels déterminé à l'exécution, vous pouvez utiliser 2 boucles. Le premier commence toutes les tâches, le second attend que tout soit fini

 console.clear();

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

/** 
 * While here we call the functions first,
 * then wait for the result later, so 
 * this will finish in 500ms.
 */
async function runTasks(timings) {
  let tasks = [];
  for (let i in timings) {
      tasks.push(wait(timings[i], `Result of task ${i}`));
  }

  /* Want fast fail? use Promise.All */
  //return Promise.All(tasks);
  
  let results = [];
  for (let task of tasks) {
       results.push(await task);
  }

  return results;
}

async function taskRunner(fn, arg, label) {
  const startTime = performance.now();
  console.log(`Task ${label} starting...`);
  let result = await fn(arg);
  console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}

void taskRunner(runTasks, [50,100,200,60,500], 'Task List'); 

1voto

Kevin Williams Points 104

En fait, je viens de faire la même chose. En utilisant promises, puis Promise.all, pour les synchroniser à la fin, vous pouvez effectuer de nombreuses demandes simultanées, mais assurez-vous de disposer de tous les résultats avant de terminer.

Voir ici dans le dernier exemple: http://javascriptrambling.blogspot.com/2017/04/to-promised-land-with-asyncawait-and.html

1voto

Nagaraja Malla Points 79

Le pseudo-code peut être écrit comme suit:

 fn async task() {
  result-1 = doAsync();
  result-2 = doAsync();
  result-n = doLongAsync();
  try{
  // handle results together
  combinedResult = handleResults(await result-1, await result-2);
  lastResult = handleLastResult(await result-n);
  }
  catch(err){
   console.error(err)
  }

}
 

résultat-1, résultat-2, résultat-n sera exécuté en parallèle. combineResult et lastResult seront également exécutés en parallèle. Cependant, la valeur CombinedResult, c'est-à-dire le retour de la fonction handleResults, sera renvoyée une fois que le résultat-1 et le résultat-2 seront disponibles et la valeur lastResult, c'est-à-dire que handleLastResult sera renvoyé une fois que le résultat-n sera disponible.

J'espère que cela t'aides

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