web-dev-qa-db-fra.com

ES6 Promises - quelque chose comme async.each?

Pour essayer de trouver quelque chose qui fonctionne exactement comme async.eachSeries, j’ai besoin d’une liste d’actions asynchrones exécutées dans l’ordre (mais pas en parallèle), mais je ne trouve pas le moyen de le faire dans l’ES6 natif. S'il vous plaît?

p.s. Je pensais aux générateurs/rendement, mais je n’ai pas encore acquis l’expérience.

Modifier 1

par demande, voici un exemple:

Supposons ce code:

let model1 = new MongooseModel({prop1: "a", prop2: "b"});
let model2 = new MongooseModel({prop1: "c", prop2: "d"});

let arr = [model1 , model2];

Maintenant, je veux le parcourir en série, pas en parallèle, donc avec le NPM "async", c'est facile:

async.eachSeries(arr, (model, next)=>{
    model.save.then(next).catch(next);
}, err=>{
    if(err) return reject(error);
    resolve();
})

Ma question est la suivante: avec ES6, puis-je le faire en mode natif? sans le package 'async' du NPM?

Modifier 2

Avec async/wait, cela peut être fait facilement:

let model1 = new MongooseModel({prop1: "a", prop2: "b"});
let model2 = new MongooseModel({prop1: "c", prop2: "d"});    

let arr = [model1 , model2];

for(let model of arr){
    await model.save();
}
24
Shlomi Sasson

Pour ceux qui aiment les réponses courtes:

[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
45
jib

Supposons que vous souhaitiez appeler une fonction asynchrone sur un tableau de données et que vous souhaitez qu’elles soient appelées de manière séquentielle, et non en parallèle.

L'interface pour async.eachSeries() est comme ceci:

eachSeries(arr, iterator, [callback])

Voici comment simuler cela avec des promesses:

// define helper function that works kind of like async.eachSeries
function eachSeries(arr, iteratorFn) {
    return arr.reduce(function(p, item) {
        return p.then(function() {
            return iteratorFn(item);
        });
    }, Promise.resolve());
}

Cela suppose que iteratorFn prend l'élément à traiter en tant qu'argument et qu'il renvoie une promesse.

Voici un exemple d'utilisation (supposant que vous avez une fs.readFileAsync() promisifiée) et une fonction appelée speak() qui renvoie une promesse lorsque vous avez terminé:

 var files = ["hello.dat", "goodbye.dat", "genericgreeting.dat"];
 eachSeries(files, function(file) {
     return fs.readFileAsync(file).then(function(data) {
         return speak(data);
     });
 });

Cela permet à l'infrastructure de promesse de tout séquencer pour vous. 


Il est également possible pour vous de séquencer les choses manuellement (bien que je ne sois pas pourquoi):

function eachSeries(arr, iteratorFn) {
    return new Promise(resolve, reject) {
        var index = 0;

        function next() {
            if (index < arr.length) {
                try {
                    iteratorFn(arr[index++]).then(next, reject);
                } catch(e) {
                    reject(e);
                }
            } else {
                resolve();
            }
        }
        // kick off first iteration
        next();
    });
}

Ou, une version plus simple qui enchaîne manuellement les promesses:

function eachSeries(arr, iteratorFn) {
    var index = 0;

    function next() {
        if (index < arr.length) {
            return iteratorFn(arr[index++]).then(next);
        }
    }
    return Promise.resolve().then(next);
}

Notez que l’une des versions manuelles doit entourer iteratorFn() avec try/catch afin de s’assurer qu’elle est à la portée de la main (conversion des exceptions en rejet). .then() est automatiquement protégé afin que les autres schémas ne soient pas obligés de capturer manuellement les exceptions, car .then() les capture déjà pour vous.

14
jfriend00

En tant qu'extension de la réponse fournie par @jib ..., vous pouvez également associer un tableau d'éléments à des fonctions asynchrones, comme suit:

[item1, item2]
    .map(item => async (prev_result) => await something_async(item))
    .reduce((p, f) => p.then(f), Promise.resolve())
    .then(() => console.log('all done'));

Notez que prev_result sera la valeur renvoyée par la précédente évaluation de something_async, ce qui correspond à peu près à un hybride entre async.eachSeries et async.waterfall.

1
Blake Regalia

// Téléchargement de ceci pour les systèmes qui exécutent une version inférieure de nodejs (Azure: /) Pas le plus court mais le plus beau que je puisse imaginer

par exemple, disons "functionWithPromise" renvoie une promesse et attend un élément.

functionWithPromise(item);

promisesArray =[];

//syncornized
itemsArray.forEach(function (item){
   promisesArray.Push(functionWithPromise(item));
});

Promise.all(promisesArray).then(function (values){
//profit
});
0
adi ben

Vous pouvez chaîner en retournant le callback then. Par exemple:

new Promise(function(resolve, reject){ 
  resolve(1)
}).then(function(v){
  console.log(v);
  return v + 1;
}).then(function(v){
  console.log(v)
});

Imprimera:


2

Cela fonctionne bien entendu lors de la résolution asynchrone des promesses:

new Promise(function(resolve, reject){
  setTimeout(function(){
    resolve(1);
  }, 1000)
}).then(function(result){
   return new Promise(function(resolve, reject){
     setTimeout(function(){
       console.log(result);
       resolve(result + 1);
     }, 1000)
   });
}).then(function(results){
  console.log(results);
});

Impression:


2

0
Kit Sunde