Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Promise.series as alternative to Promise.all

I saw this example implementation of Promise.all - which runs all promises in parallel - Implementing Promise.all

Note that the functionality I am looking for is akin to Bluebird's Promise.mapSeries http://bluebirdjs.com/docs/api/mapseries.html

I am making an attempt at creating Promise.series, I have this which seems to work as intended (it actually is totally wrong, don't use it, see answers):

Promise.series = function series(promises){

    return new Promise(function(resolve,reject){

    const ret = Promise.resolve(null);
    const results = [];

    promises.forEach(function(p,i){
         ret.then(function(){
            return p.then(function(val){
               results[i] = val;
            });
         });
    });

    ret.then(function(){
         resolve(results);
    },
     function(e){
        reject(e);
     });

    });

}


Promise.series([
    new Promise(function(resolve){
            resolve('a');
    }),
    new Promise(function(resolve){
            resolve('b');
    })
    ]).then(function(val){
        console.log(val);
    }).catch(function(e){
        console.error(e.stack);
    });

However, one potential problem with this implementation is that if I reject a promise, it doesn't seem to catch it:

 Promise.series([
    new Promise(function(resolve, reject){
            reject('a');   // << we reject here
    }),
    new Promise(function(resolve){
            resolve('b');
    })
    ]).then(function(val){
        console.log(val);
    }).catch(function(e){
        console.error(e.stack);
    });

does anyone know why the error doesn't get caught and if there is a way to fix this with Promises?

According to a comment, I made this change:

Promise.series = function series(promises){

    return new Promise(function(resolve,reject){

    const ret = Promise.resolve(null);
    const results = [];

    promises.forEach(function(p,i){
         ret.then(function(){
            return p.then(function(val){
               results[i] = val;
            },
            function(r){
                console.log('rejected');
                reject(r);   // << we handle rejected promises here
            });
         });
    });

    ret.then(function(){
         resolve(results);
    },
     function(e){
        reject(e);
     });

    });

}

but this still doesn't work as expected...

like image 341
Alexander Mills Avatar asked Jun 01 '16 21:06

Alexander Mills


1 Answers

The promise returned by then in the forEach loop does not handle potential errors.

As pointed out in a comment by @slezica, try to use reduce rather than forEach, this chains all promises together.

Promise.series = function series(promises) {
    const ret = Promise.resolve(null);
    const results = [];

    return promises.reduce(function(result, promise, index) {
         return result.then(function() {
            return promise.then(function(val) {
               results[index] = val;
            });
         });
    }, ret).then(function() {
        return results;
    });
}

Keep in mind that the promises are already "running" at that point though. If you truly want to run your promises in series, you should adjust your function and pass in an array of functions that return promises. Something like this:

Promise.series = function series(providers) {
    const ret = Promise.resolve(null);
    const results = [];

    return providers.reduce(function(result, provider, index) {
         return result.then(function() {
            return provider().then(function(val) {
               results[index] = val;
            });
         });
    }, ret).then(function() {
        return results;
    });
}
like image 128
forrert Avatar answered Sep 24 '22 02:09

forrert