Tyler James
Tyler James

Reputation: 99

Simplifying a Promise Chain

I'm trying to simplify Promise chains as much as possible by having a function handle an array of functions. They need to be in order, but my method of giving them the proper syntax isn't working, and I'm sure there's a better way to be doing this. Each function in the chain calls the database and needs to trigger the next function when it returns.

// Run each in order.
var Chain = function(chain, cb){
    chain.forEach(function(i){  i = function(r){return new Promise(i)};   });
    chain.reduce(function(cur, next) { {cur.then(next)} }, Promise.resolve()).then(function() { cb() });
}
Chain([

    r=>{ 
        UserData('elle',  i=>{ 
            console.log(i);
            r(i)
        }) 
    },
    r=>{ 
        UserData('tyler', {age:"22"}, i=>{ 
            console.log(i);
            r(i);
        }) 
    },
    r=>{ 
        UserData('nerd',  i=>{ 
            console.log(i);
            r(i) 
        }) 
    },
    r=>{ 
        UserData('jeax',  i=>{ 
            console.log(i);
            r(i) 
        }) 
    }

], function(){

    console.log("Done.");

});

The example below works how I need it, just not using the Chain function that i desire with the simplicity of each item.

var PChain = function(cb){
    // Convert each item.
    return function(){ return new Promise(r=>{cb(r)}) };
}
// The items.
var chain = [
    PChain(r=>{ 
        UserData('elle',  i=>{ 
            console.log(i);r(i)
        }) 
    }),
    PChain(r=>{ 
        UserData('tyler', {age:"22"}, i=>{ 
            console.log(i);r(i);
        }) 
    }),
    PChain(r=>{ 
        UserData('nerd',  
            i=>{ console.log(i);r(i) 
        }) 
    }),
    PChain(r=>{ 
        UserData('jeax',  
            i=>{ console.log(i);r(i) 
        }) 
    })
];
chain.reduce(function(cur, next) { return cur.then(next) }, Promise.resolve()).then(function() {
    //all executed
    console.log("Done.");
});

Upvotes: 0

Views: 757

Answers (2)

Bergi
Bergi

Reputation: 664385

Your mistake is in the Chain function:

chain.forEach(function(i){  i = function(r){return new Promise(i)};   });
chain.reduce(function(cur, next) { {cur.then(next)} }, Promise.resolve()).then(function() { cb() });

Specifically, assigning to i inside a forEach does absolutely nothing. What you actually want is to use map:

chain.map(function(exec){ return function(r){ return new Promise(exec)}; }).reduce(…);

where map returns an array of the functions that you then can reduce over.


Regardless, what you actually should do is to promisify the UserData function:

function user(name, options) {
    return new Promise((resolve, reject) => {
        if (options)
            UserData(name, options, resolve);
        else
            UserData(name, resolve);
    });
}

with which you don't even need a chain but can simply write

user('elle').then(i => { 
    console.log(i);
    return user('tyler', {age:"22"});
}).then(i => { 
    console.log(i);
    return user('nerd');
}).then(i => {
    console.log(i);
    return user('jeax');
}).then(i => {
    console.log(i);
}).then(function() {
   //all executed
   console.log("Done.");
});

You still can use reduce if you have a dynamically-sized array of user names, but you should not use an array of functions.

Upvotes: 2

Ivan Gabriele
Ivan Gabriele

Reputation: 6900

You should use Promise.all(iterable) :

Promise.all([r1, r2, r3]).then(function(values) { 
  // Do something
});

UPDATE

To respect a specific order, you should check this answer. There is unfortunately no ES6 method able to do that natively.

One of the most used JS promise queuing system (among others, by Angular) is Kris Kowal's Q and it does recommend something similar, so this is not so far from what your wrote.

NOTE

In your code, it seems that you're trying to achieve some sort of batch requests. You have to know that it's really scarce to really need an ordered promises chain in JS.

  1. If you using that for steams (like file contents operations), it's better to use pipes.
  2. If you're using that for batch/filtered API requests :
    • If you control the API : change your API to manage batch/filtered requests
    • If you don't control it : create a middle-ware application managing it

Upvotes: 1

Related Questions