ES6 Promises - something like async.each?

2019-01-04 02:14发布

Trying to figure-out how to find something that functional exactly like async.eachSeries, i need a list of async actions run in sequence (not in parallel) but can't find a way to do it in native ES6, can anyone advise, please?

p.s. thought about generators/yield but don't have the experience yet so I'm not realized how exactly it can help me.

Edit 1

per request, here is an example:

Assume this code:

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

let arr = [model1 , model2];

Now, I want to run over it in a series, not parallel, so with the "async" NPM it's easy:

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

My question is: with ES6, can I do it natively? without the NPM 'async' package?

Edit 2

With async/await it can be done easily:

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();
}

5条回答
2楼-- · 2019-01-04 02:30

As an extension to the answer provided by @jib... you can also map an array of items to async functions like so:

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

Notice how prev_result will be the value returned by the previous evaluation of something_async, this is roughly equivalent to a hybrid between async.eachSeries and async.waterfall.

查看更多
Animai°情兽
3楼-- · 2019-01-04 02:36

For those who like short answers:

[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());
查看更多
混吃等死
4楼-- · 2019-01-04 02:50

Let's say you want to call some async function on an array of data and you want them called sequentially, not in parallel.

The interface for async.eachSeries() is like this:

eachSeries(arr, iterator, [callback])

Here's how to simulate that with promises:

// 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());
}

This assumes that iteratorFn takes the item to process as an argument and that it returns a promise.

Here's a usage example (that assumes you have a promisified fs.readFileAsync()) and have a function called speak() that returns a promise when done:

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

This lets the promise infrastructure sequence everything for you.


It is also possible for you to sequence things manually (though I'm not sure why):

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();
    });
}

Or, a simpler version that manually chains the promises together:

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

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

Note how one of the manual versions has to surround iteratorFn() with try/catch in order to make sure it is throw-safe (convert exceptions into a rejection). .then() is automatically throw safe so the other schemes don't have to manually catch exceptions since .then() already catches them for you.

查看更多
Deceive 欺骗
5楼-- · 2019-01-04 02:52

You can chain by returning in the then callback. For instance:

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

Will print:

1
2

This of course work when asynchronously resolving promises:

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);
});

Printing:

1
2

查看更多
我只想做你的唯一
6楼-- · 2019-01-04 02:53

//Uploading this for systems that run lower nodejs version (Azure :/) Not the shortest but the nicest i can think of

for example lets say "functionWithPromise" returns some promise and expects some item.

functionWithPromise(item);

promisesArray =[];

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

Promise.all(promisesArray).then(function (values){
//profit
});
查看更多
登录 后发表回答