Considérez le code suivant qui lit un tableau de fichiers de manière série / séquentielle. readFiles
renvoie une promesse, qui n'est résolue qu'une fois que tous les fichiers ont été lus dans l'ordre.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
Le code ci-dessus fonctionne, mais je n'aime pas avoir à faire de récursivité pour que les choses se produisent séquentiellement. Existe-t-il un moyen plus simple de réécrire ce code pour ne pas avoir à utiliser ma readSequential
fonction bizarre ?
À l'origine, j'ai essayé d'utiliser Promise.all
, mais cela a provoqué tous les readFile
appels simultanément, ce qui n'est pas ce que je veux:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
la source
la source
readFileSequential()
est déjà retourné avant que le suivant soit appelé (car c'est asynchrone, il se termine longtemps après que l'appel de fonction d'origine est déjà revenu).Réponses:
Mise à jour 2017 : j'utiliserais une fonction asynchrone si l'environnement la supporte:
Si vous le souhaitez, vous pouvez différer la lecture des fichiers jusqu'à ce que vous en ayez besoin à l'aide d'un générateur asynchrone (si votre environnement le prend en charge):
Mise à jour: À la réflexion - je pourrais utiliser une boucle for à la place:
Ou plus compact, avec réduire:
Dans d'autres bibliothèques de promesses (comme quand et Bluebird), vous avez des méthodes utilitaires pour cela.
Par exemple, Bluebird serait:
Bien qu'il n'y ait vraiment aucune raison de ne pas utiliser async, attendez aujourd'hui.
la source
Promise.resolve(Promise.resolve(15))
est identique àPromise.resolve(15)
.Voici comment je préfère exécuter les tâches en série.
la source
result = result.then(task);
Cette question est ancienne, mais nous vivons dans un monde d'ES6 et de JavaScript fonctionnel, alors voyons comment nous pouvons nous améliorer.
Parce que les promesses s'exécutent immédiatement, nous ne pouvons pas simplement créer un tableau de promesses, elles se déclencheraient toutes en parallèle.
Au lieu de cela, nous devons créer un tableau de fonctions qui renvoie une promesse. Chaque fonction sera alors exécutée séquentiellement, ce qui commencera ensuite la promesse à l'intérieur.
Nous pouvons résoudre ce problème de plusieurs manières, mais ma façon préférée est d'utiliser
reduce
.Cela devient un peu difficile à utiliser
reduce
en combinaison avec des promesses, j'ai donc décomposé la doublure en quelques petites piqûres digestibles ci-dessous.L'essence de cette fonction est d'utiliser à
reduce
partir d'une valeur initiale dePromise.resolve([])
, ou une promesse contenant un tableau vide.Cette promesse sera ensuite passée dans la
reduce
méthode aspromise
. C'est la clé pour enchaîner chaque promesse de manière séquentielle. La prochaine promesse à exécuter estfunc
et lorsque lesthen
incendies se produisent, les résultats sont concaténés et cette promesse est ensuite renvoyée, exécutant lereduce
cycle avec la fonction de promesse suivante.Une fois toutes les promesses exécutées, la promesse retournée contiendra un tableau de tous les résultats de chaque promesse.
Exemple ES6 (un liner)
Exemple ES6 (ventilé)
Usage:
la source
Array.prototype.concat.bind(result)
c'est la partie qui me manquait, a dû faire pousser les résultats manuellement qui a fonctionné mais était moins coolconsole.log.bind(console)
déclaration dans votre dernier exemple est maintenant généralement inutile. Ces jours-ci, vous pouvez simplement passerconsole.log
. Par exemple.serial(funcs).then(console.log)
. Testé sur nodejs actuel et Chrome.Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))
et ainsi de suitePour ce faire simplement dans ES6:
la source
files.forEach
si les fichiers sont un tableau.for (file of files) {...}
.Promise.resolve()
pour créer une promesse déjà résolue dans la vie réelle. Pourquoi pas?Promise.resolve()
semble plus propre quenew Promise(success => success())
.Promise.resolve();
dans votre code.return sequence;
je metssequence.then(() => { do stuff });
Utilisation simple pour la promesse standard de Node.js:
METTRE À JOUR
items-promise est un package NPM prêt à l'emploi faisant de même.
la source
J'ai dû exécuter beaucoup de tâches séquentielles et j'ai utilisé ces réponses pour forger une fonction qui se chargerait de gérer n'importe quelle tâche séquentielle ...
La fonction prend 2 arguments + 1 optionnel. Le premier argument est le tableau sur lequel nous allons travailler. Le deuxième argument est la tâche elle-même, une fonction qui renvoie une promesse, la tâche suivante ne sera lancée que lorsque cette promesse sera résolue. Le troisième argument est un rappel à exécuter lorsque toutes les tâches ont été effectuées. Si aucun rappel n'est passé, la fonction renvoie la promesse qu'elle a créée afin que nous puissions gérer la fin.
Voici un exemple d'utilisation:
J'espère que cela fera gagner du temps à quelqu'un ...
la source
La plus belle solution que j'ai pu trouver était avec des
bluebird
promesses. Vous pouvez simplement faire enPromise.resolve(files).each(fs.readFileAsync);
sorte que les promesses soient résolues séquentiellement dans l'ordre.la source
Promise.each(filtes, fs.readFileAsync)
. Btw, tu n'as pas à faire.bind(fs)
?new Array(int)
. Tout ce qui est fait est de prédéfinir lalength
paire clé-valeur, affectant le nombre d'indices utilisés pendant l'itération basée sur la longueur. Il a zéro effet sur l'indexation ou les limites d'index du tableau)Il s'agit d'une légère variation d'une autre réponse ci-dessus. Utilisation de promesses natives:
Explication
Si vous avez ces tâches
[t1, t2, t3]
, alors ce qui précède est équivalent àPromise.resolve().then(t1).then(t2).then(t3)
. C'est le comportement de réduire.Comment utiliser
Tout d' abord , vous devez construire une liste de tâches! Une tâche est une fonction qui n'accepte aucun argument. Si vous devez transmettre des arguments à votre fonction, utilisez
bind
ou d'autres méthodes pour créer une tâche. Par exemple:la source
Ma solution préférée:
Ce n'est pas fondamentalement différent des autres publiés ici, mais:
Exemple d'utilisation:
Testé sur Chrome (v59) et NodeJS (v8.1.2) actuels raisonnables.
la source
Utilisez
Array.prototype.reduce
, et n'oubliez pas d'envelopper vos promesses dans une fonction sinon elles seront déjà en cours d'exécution!agréable et facile ... vous devriez pouvoir réutiliser la même graine pour la performance, etc.
Il est important de se prémunir contre les tableaux vides ou les tableaux avec seulement 1 élément lors de l'utilisation de réduire , donc cette technique est votre meilleur pari:
puis appelez-le comme:
la source
J'ai créé cette méthode simple sur l'objet Promise:
Créez et ajoutez une méthode Promise.sequence à l'objet Promise
Usage:
La meilleure chose à propos de cette extension à l'objet Promise, c'est qu'elle est cohérente avec le style des promesses. Promise.all et Promise.sequence sont invoqués de la même manière, mais ont une sémantique différente.
Mise en garde
L'exécution séquentielle des promesses n'est généralement pas un très bon moyen d'utiliser les promesses. Il est généralement préférable d'utiliser Promise.all et de laisser le navigateur exécuter le code le plus rapidement possible. Cependant, il existe de réels cas d'utilisation pour cela - par exemple lors de l'écriture d'une application mobile en utilisant javascript.
la source
Promise.all
et votrePromise.sequence
. L'un prend un itérable de promesses, l'autre prend un tableau de fonctions qui renvoient des promesses.reduce
comme dans la réponse de Benjamin est juste beaucoup plus simple.Vous pouvez utiliser cette fonction qui obtient la liste promiseFactories:
Promise Factory est une simple fonction qui renvoie une promesse:
Cela fonctionne parce qu'une fabrique de promesses ne crée pas la promesse tant qu'elle n'est pas demandée. Cela fonctionne de la même manière qu'une fonction then - en fait, c'est la même chose!
Vous ne voulez pas du tout opérer sur un éventail de promesses. Selon la spécification Promise, dès qu'une promesse est créée, elle commence à s'exécuter. Donc, ce que vous voulez vraiment, c'est un éventail d'usines prometteuses ...
Si vous voulez en savoir plus sur les promesses, vous devriez vérifier ce lien: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
la source
Ma réponse basée sur https://stackoverflow.com/a/31070150/7542429 .
Cette solution renvoie les résultats sous forme de tableau comme Promise.all ().
Usage:
la source
J'ai vraiment aimé la réponse de @ joelnet, mais pour moi, ce style de codage est un peu difficile à digérer, j'ai donc passé quelques jours à essayer de comprendre comment j'exprimerais la même solution de manière plus lisible et c'est ma prendre, juste avec une syntaxe différente et quelques commentaires.
la source
Comme Bergi l'a remarqué, je pense que la meilleure solution et claire est d'utiliser BlueBird.each, code ci-dessous:
la source
Tout d'abord, vous devez comprendre qu'une promesse est exécutée au moment de la création.
Ainsi, par exemple, si vous avez un code:
Vous devez le changer pour:
Ensuite, nous devons enchaîner séquentiellement les promesses:
en cours d'exécution
after()
, s'assurera que la promesse n'est créée (et exécutée) que le moment venu.la source
J'utilise le code suivant pour étendre l'objet Promise. Il gère le rejet des promesses et renvoie un tableau de résultats
Code
Exemple
la source
Si vous le souhaitez, vous pouvez utiliser réduire pour faire une promesse séquentielle, par exemple:
cela fonctionnera toujours en séquence.
la source
Utilisation de l'ES moderne:
la source
Avec Async / Await (si vous avez le support d'ES7)
(vous devez utiliser la
for
boucle, et nonforEach
parce que async / wait a des problèmes lors de l'exécution de la boucle forEach)Sans Async / Await (en utilisant Promise)
la source
forEach
(selon cela )Sur la base du titre de la question, "Résoudre les promesses l'une après l'autre (c'est-à-dire en séquence)?", Nous pourrions comprendre que le PO est plus intéressé par le traitement séquentiel des promesses lors du règlement que par les appels séquentiels en soi .
Cette réponse est offerte:
Si les appels simultanés ne sont vraiment pas souhaités, consultez la réponse de Benjamin Gruenbaum qui couvre de manière exhaustive les appels séquentiels (etc.).
Si toutefois, vous êtes intéressé (pour de meilleures performances) par des modèles qui permettent des appels simultanés suivis d'un traitement séquentiel des réponses, alors lisez la suite.
Il est tentant de penser que vous devez utiliser
Promise.all(arr.map(fn)).then(fn)
(comme je l'ai fait plusieurs fois) ou le sucre fantaisie d'une librairie Promise (notamment Bluebird's), cependant (avec le mérite de cet article ) unarr.map(fn).reduce(fn)
modèle fera l'affaire, avec les avantages qu'il:.then()
est utilisé.Le voici, écrit pour
Q
.Remarque: seul un fragment
Q()
,, est spécifique à Q. Pour jQuery, vous devez vous assurer que readFile () renvoie une promesse jQuery. Avec A + libs, les promesses étrangères seront assimilées.La clé ici est la
sequence
promesse de réduction , qui séquence le traitement desreadFile
promesses mais pas leur création.Et une fois que vous avez absorbé cela, c'est peut-être légèrement époustouflant lorsque vous réalisez que la
.map()
scène n'est pas vraiment nécessaire! L'ensemble du travail, les appels parallèles plus la gestion en série dans le bon ordre, peut être réalisé avecreduce()
seul, plus l'avantage supplémentaire d'une flexibilité supplémentaire pour:Le voici,
Q
encore une fois.C'est le schéma de base. Si vous vouliez également fournir des données (par exemple, les fichiers ou une transformation de ceux-ci) à l'appelant, vous auriez besoin d'une variante légère.
la source
sequence.then(() => filePromise)
chose est un contre-modèle - elle ne propage pas les erreurs dès qu'elles le peuvent (et créeunhandledRejection
dans les bibliothèques qui les prennent en charge). Vous devriez plutôt utiliserQ.all([sequence, filePromise])
ou$.when(sequence, filePromise)
. Certes, ce comportement peut être ce que vous voulez lorsque vous essayez d'ignorer ou d'ignorer les erreurs, mais vous devez au moins le mentionner comme un inconvénient.unhandledRejection
événements. Dans Bluebird, vous pouvez contourner cela en utilisantsequence.return(filePromise)
qui a le même comportement mais gère très bien les rejets. Je ne connais aucune référence, je viens juste de la trouver - je ne pense pas que le "(anti) pattern" ait encore un nom.Votre approche n'est pas mauvaise, mais elle a deux problèmes: elle avale les erreurs et elle utilise l'antipattern Explicit Promise Construction.
Vous pouvez résoudre ces deux problèmes et rendre le code plus propre, tout en utilisant la même stratégie générale:
la source
Si quelqu'un d'autre a besoin d'un moyen garanti de manière séquentielle STRICTEMENT de résolution des promesses lors de l'exécution des opérations CRUD, vous pouvez également utiliser le code suivant comme base.
Tant que vous ajoutez «return» avant d'appeler chaque fonction, décrivant une promesse, et utilisez cet exemple comme base, le prochain appel de fonction .then () démarrera de manière cohérente après la fin de la précédente:
la source
La méthode push and pop de tableau peut être utilisée pour la séquence de promesses. Vous pouvez également pousser de nouvelles promesses lorsque vous avez besoin de données supplémentaires. Voici le code que j'utiliserai dans React Infinite Loader pour charger une séquence de pages.
la source
La plupart des réponses n'incluent pas les résultats de TOUTES les promesses individuellement, donc si quelqu'un cherche ce comportement particulier, c'est une solution possible en utilisant la récursivité.
Il suit le style de
Promise.all
:Renvoie le tableau des résultats dans le
.then()
rappel.Si une promesse échoue, elle est retournée immédiatement dans le
.catch()
rappel.Remarque sur la
tasks
déclaration de tableau :Dans ce cas, il n'est pas possible d'utiliser la notation suivante comme le
Promise.all
ferait:Et nous devons utiliser:
La raison en est que JavaScript commence à exécuter la promesse immédiatement après sa déclaration. Si nous utilisons des méthodes comme
Promise.all
, il vérifie simplement que l'état de chacun d'eux estfulfilled
ourejected
, mais ne démarre pas l'exection elle-même. En utilisant() => promise()
nous arrêtons l'exécution jusqu'à son appel.la source
Ici, la clé est de savoir comment vous appelez la fonction veille. Vous devez passer un tableau de fonctions qui renvoie lui-même une promesse au lieu d'un tableau de promesses.
la source
Il s'agit d'étendre la façon de traiter une séquence de promesses de manière plus générique, en prenant en charge des séquences dynamiques / infinies, basées sur la mise en œuvre de spex.sequence :
Non seulement cette solution fonctionnera avec des séquences de n'importe quelle taille, mais vous pourrez facilement y ajouter une limitation des données et un équilibrage de charge .
la source