Bluebird's Promise.all() method when one promise is dependent on another
Asked Answered
A

3

8

I'm writing some code that currently looks like this because I have dependencies in my code. I was wondering if there was a cleaner way to do this with Promise.all()? Here is my pseudo code:

        return someService.getUsername()
            .then(function(username) {
                user = username;
            })
            .then(function() {
                return someService.getUserProps(user);
            })
            .then(function(userProps) {
                userProperties = userProps;
                return someService.getUserFriends(user);
            })
            .then(function(userFriends) {
                friends = userFriends;
            })
            .catch(error)
            .finally(function(){
                // do stuff with results
            });

The important thing is that I need user before I can make the second two calls for getUserProps() and getUserFriends(). I thought I could use Promise.all() for this like so:

var user = someService.getUsername()
    .then(function(username) {
        user = username;
    })
var getUserProps = someService.getUserProps(user);
var getUserProps = someService.getUserFriends(user);

return Promise.all(user, getUserProps, getUserFriends, function(user, props, friends) {
    // do stuff with results
})

But I cannot get this to work. Is this the correct case to use .all?

Acescent answered 17/9, 2014 at 16:16 Comment(0)
A
15

Promise.all() is designed for parallel operation where you launch a bunch of async operations to run at the same time and then it tells you when they are all done.

It does not sequence one versus the completion of another in any way. So, you can't use it to wait for the user to be ready and then have the other operations use that user. It just isn't designed to do that.

You could get the user first and then when that is complete, you could use Promise.all() with your other two operations which I think can be run at the same time and don't depend upon each other.

var user;
someService.getUsername().then(function(username) {
    user = username;
    return Promise.all(getUserProps(user), getUserFriends(user));
}).then(function() {
    // do stuff with results array
}).catch(function() {
    // handle errors
});
Abutment answered 17/9, 2014 at 16:36 Comment(0)
B
4

You can use .all but you're going to have to make sure they run sequentially your code, you can do this by .thening them like you've done. If you do that you should use .join which is a shorthand for .all([...]).spread(....

var user = someService.getUsername();
var props = user.then(getUserProps)
var friends = user.then(getUserFriends)
Promise.join(user, props, friends, function(user, props, friends) {

    // everything is available here, everything is synchronized
});

If what you were trying to solve is the closure/nesting issue - then this is the way to do so.

Bradstreet answered 17/9, 2014 at 17:20 Comment(4)
This didn't seem to work for me because I need to pass a variable to getUserProps() and I don't have it when I set the props variable on the second line.Acescent
@Acescent the user variable is automatically passed to the getUserProps function as it's the promise resolution value - just like in your example. You can use .bind if you need to pass additional parameters or an anonymous function.Bradstreet
ohhh.... so the props definition could have been: var props = user.then(getUserProps(user)); ?Acescent
No, since user is a promise that already resolves with the username. It's simply user.then(getUserProps) which does the same thing as user.then(function(user){ return getUserProps(user); }) only shorter and more concise. The point here is that the promise acts as a proxy for the value directly.Bradstreet
S
2

Promise.all() is a way to execute a list of promises in parallel but if we want to execute a list of promises in a series where one is dependent on the other, we've to solve it a bit differently

// Promise returning functions to execute
function doFirstThing(){ return Promise.resolve(1); }  
function doSecondThing(res){ return Promise.resolve(res + 1); }  
function doThirdThing(res){ return Promise.resolve(res + 2); }  
function lastThing(res){ console.log("result:", res); }

var fnlist = [ doFirstThing, doSecondThing, doThirdThing, lastThing];

// Execute a list of Promise return functions in series
function pseries(list) {  
  var p = Promise.resolve();
  return list.reduce(function(pacc, fn) {
    return pacc = pacc.then(fn);
  }, p);
}

pseries(fnlist);  
// result: 4
Spillage answered 14/4, 2016 at 19:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.