16

I want to make multiple requests in node.js to get a couple of external API responses, to combine them into one array. I'm using a for loop to achieve this. Here's my code:

res.setHeader('Content-Type', 'application/json');
const sub = req.query.days_subtract;
const enddate = req.query.end_date; 

var array = [];

for (var i = 0; i < sub; i++) {
  request("https://api.nasa.gov/planetary/apod?date=" + subtractDate(enddate, i) + "&api_key=DEMO_KEY", function(error, response, body) {
    array.push(body); 
    // console.log(body);
  });
}
res.send(array);

but this piece of code returns [] all the time. I know it's because the for loop only starts these async requests, but it doesn't wait for them to finish. I tried to use async/await but that didn't work either. So how to wait for this loop to finish getting requests and to finish pushing them to the array so it can be shown to users?

2
  • attach code with async await, it should work or use sync-request library Commented Oct 9, 2018 at 15:35
  • You can use promises: promisejs.org Commented Oct 9, 2018 at 15:35

4 Answers 4

28

For your use case, using await with Promise.all is probably the most performant way to go about it. Your code should look something like this:

res.setHeader('Content-Type', 'application/json');
const sub = req.query.days_subtract;
const enddate = req.query.end_date;

var promiseArray = [];

for (var i = 0; i < sub; i++) {
    promiseArray.push(new Promise((resolve, reject) => {
        request("https://api.nasa.gov/planetary/apod?date=" + subtractDate(enddate, i) + "&api_key=DEMO_KEY", function(error, response, body) {
        if (error) reject(error);
        else resolve(body)
    })
  }))
}
res.send(await Promise.all(promiseArray));
Sign up to request clarification or add additional context in comments.

Comments

9

Use something like request-promise. If you await each request in the for loop, then all requests are done in series. If each request is independent and doesn't need to be done one after the other, this is inefficient. The best way to handle something like this is to do them all in parallel and then wait for them all to complete. This is done by creating an array of request promises and then using await Promise.all(promiseArray) to wait for all promises to resolve.

var promises = [];

for (var i = 0; i < sub; i++) {
  const promise = request("https://api.nasa.gov/planetary/apod?date=" + subtractDate(enddate, i) + "&api_key=DEMO_KEY");
  promises.push(promise);
}

const array = await Promise.all(promises);

5 Comments

request does not return a promise by default
That's why I suggested using request-promise.
ah nvm, focused on the code not the text, apologies
great bro.. its very simple
Using request-promise indeed make the code cleaner and more readable
2

To use async/await, you need to use a HTTP library that returns promises instead of using callbacks.

It's well worth using a library that can do this. A good option for a different library is node-fetch, which effectively implements the "Fetch API" as implemented by browsers.

You can then simply await fetch(url).

async/await works so well and is becoming so common-place, I can highly recommend switching to frameworks and libraries that treat it as a first-class citizen instead of an afterthought. This includes Express, which is also a callback-based library, instead of a Promise-based one. But that's slightly off-topic.

Comments

2

As others have said, you should probably use Promise for your asynchronous code, and the async / await syntax is very legible.

However, you seem to prefer using callbacks. In that case, you could write your code this way :

res.setHeader('Content-Type', 'application/json');
const sub = req.query.days_subtract;
const enddate = req.query.end_date; 

var array = [];
var resultsCount = 0;

for (var i = 0; i < sub; i++) {
  request("https://api.nasa.gov/planetary/apod?date=" + subtractDate(enddate, i) + "&api_key=DEMO_KEY", function(error, response, body) {
    if (error) { res.status(500) }
    array[i] = body
    // console.log(body);
    resultCount++;
    if (resultCount === sub) {
      res.send(array);
    }
  });
}

Basically, the idea is to call the res.send method only once all requests have returned their result.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.