How do I make multiple async calls, then execute a function after all calls have completed?

Viewed 244

I have a nodeJS web server that calls another api to get data, then (ideally) returns that data to the web page. I am using the request-promise library and trying to use Promise.all() to no avail.

What I have so far:

app.get('/endpoint', (req, res) => {
  var url = 'some url';
  var dataObject = {};
  var promise1 = rp(url).then( (data) => {
    //process data into dataObject
  }
  url = 'another url';
  var promise2 = rp(url).then( (data) => {
    //process data into dataObject
  }
  //same thing for promise 3...
  Promise.all([promise1, promise2, promise3]).then(res.send(dataObject));
});

The problem is that res.send(dataObject) is not waiting for the promises to finish, so it's sending an empty object. I'm not very familiar with promises and am trying to learn more about them through this, but things aren't doing what I'm expecting. If I were to guess at what's happening, when calling rp().then(), .then() is causing the promise to resolve, and then it executes the code inside .then() after "resolving".

What I want is the code inside .then() to execute, then once all promises have finished their individual data processing, I want it to execute the .all().then() to return that data, but how would I go about doing this?

2 Answers

.then() needs to be passed a function reference. You aren't passing it that. Change this:

Promise.all([promise1, promise2, promise3]).then(res.send(dataObject));

to this:

Promise.all([promise1, promise2, promise3]).then(data => res.send(dataObject));

Your version of the code is executing res.send(dataObject) immediately and then passing the return value from that to .then(). You have to pass a function reference to .then() so the promise infrastructure can call that function some time later.


To illustrate, your version of the code works similar to this:

let f = res.send(dataObject);
Promise.all([promise1, promise2, promise3]).then(f);

As you can clearly see, you're executing res.send() BEFORE Promise.all().then() can do it's thing.

Instead, you want to do something like this:

function f() {
    res.send(dataObject);
}
Promise.all([promise1, promise2, promise3]).then(f);

Where you are clearly passing a function that can be called later to .then(). The shortened version of this is:

Promise.all([promise1, promise2, promise3]).then(data => res.send(dataObject));

Side note: you could use async/await like the following, which adapting such a solution might have prevented your mistake as another answer pointed out already.

app.get('/endpoint', async (req, res) => {
  var url = 'some url';
  var dataObject = {};
  var promise1 = rp(url).then( (data) => {
    //process data into dataObject
  });
  url = 'another url';
  var promise2 = rp(url).then( (data) => {
    //process data into dataObject
  });
  //same thing for promise 3...

  let dataObject = await Promise.all([promise1, promise2, promise3]);
  res.send(dataObject);
});
Related