Promise.all vs boucle itérative
Un réflexe qu'il faut intégrer depuis la prolifération de l'opérateur await
: quand c'est possible, toujours préférer l'usage de Promise.all
à celui d'une boucle itérative ; l'exécution peut être (bien) plus rapide. Ça parait évident quand on le dit mais c'est parfois difficile de se débarrasser de certaines habitudes.
Prenons un traitement fictif asynchrone que l'on simule avec un délai :
/**
* @param {number} index
*/
const doTask = async index => new Promise(resolve => {
return window.setTimeout(() => {
console.log(`doAsync ${index}`);
resolve(index);
}, 1000);
});
La promesse de la méthode doTask
est résolue au mieux au bout de une seconde.
Si on a besoin d'effectuer cette tâche disons trois fois, on peut le faire dans une boucle itérative :
(async => {
for (let i = 0; i < 3; i++) {
await doTask(i);
}
})();
Ce code prendra environ trois secondes à s'exécuter. Par contre, si on exécute la série de tâches avec Promise.all
:
(async => {
await Promise.all([0,1,2].map(i => doAsync(i)));
})();
le traitement ne prendra plus qu'un peu plus d'une seconde.
Énorme différence d'exécution qui vient du fait que, dans le cas de la boucle itérative, une tâche est exécutée uniquement quand la précédente est terminé tandis qu'avec Promise.all
, les tâches sont lancées de manière concurrentes ; la durée globale dépend alors du « temps de cerveau disponible » de la machine.
Donc, quand on a une série de tâches qui ne dépendent pas les unes des autres, dont le résultat de l'une n'est pas la point d'entrée de l'autre par exemple, alors il est fortement recommandé d'utiliser Promise.all
au lieu d'une boucle itérative. Surtout que le truc bien avec Promise.all
c'est que le résultat des promesses reste dans le même ordre, peut importe la promesse qui se sera résolu le plus vite.