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:
- pour démontrer que les appels séquentiels ne sont pas nécessaires pour le traitement séquentiel des réponses.
- pour exposer des modèles alternatifs viables aux visiteurs de cette page - y compris l'OP s'il est toujours intéressé plus d'un an plus tard.
- malgré l'affirmation du PO selon laquelle il ne veut pas passer d'appels simultanément, ce qui peut être vraiment le cas mais peut également être une hypothèse basée sur le désir de traitement séquentiel des réponses comme le titre l'indique.
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 ) un arr.map(fn).reduce(fn)
modèle fera l'affaire, avec les avantages qu'il:
- fonctionne avec n'importe quelle bibliothèque de promesses - même les versions pré-conformes de jQuery - uniquement
.then()
est utilisé.
- offre la possibilité de sauter une erreur ou d'arrêter une erreur, selon ce que vous voulez avec un mod d'une ligne.
Le voici, écrit pour Q
.
var readFiles = function(files) {
return files.map(readFile) //Make calls in parallel.
.reduce(function(sequence, filePromise) {
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
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 des readFile
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é avec reduce()
seul, plus l'avantage supplémentaire d'une flexibilité supplémentaire pour:
- convertir des appels asynchrones parallèles en appels asynchrones série en déplaçant simplement une ligne - potentiellement utile pendant le développement.
Le voici, Q
encore une fois.
var readFiles = function(files) {
return files.reduce(function(sequence, f) {
var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
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.