JavaScript: How to make API calls for each value in an array and get an array of results.
Today I had to call an API with for a bunch of ids and get the results for each of those ids. Now the problem is, I need to collect the results from all of those API calls. And I did not want to use plain old for loop for this today.
So here’s how I did it. (Skip to the end for full code)
Promise
new Promise(executor)
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
executor
A function that is passed with the arguments resolve and reject. The executor normally initiates some asynchronous work, and then, once that completes, either calls the resolve function to resolve the promise or else rejects it if an error occurred.
let requests = ids.map(id => {
return new Promise((resolve, reject) => {
request({
uri: <API url>+'?id=' + id,
method: 'GET'
})
})
})
So now each of our API calls is wrapped in a promise. That means requests is now an iterable containing promises.
Promise.all()
Promise.all() returns a single Promise that resolves when all of the promises passed as an iterable have resolved.
Now we pass the requests array to Promise.all().
Promise.all(requests)
Now, the bits and pieces are ready. But I hadn’t yet connected these two parts.
Promise.all() returns a pending promise when passed with an iterable containing promises. This returned promise is then resolved/rejected asynchronously (as soon as the stack is empty) when all the promises in the given iterable have resolved, or if any of the promises reject.
So that means there needs to be
- A way to resolve each of the promises in the iterable
- The code to be executed when the promise returned by Promise.all() has resolved
1. A way to resolve each of the promises in the iterable
The executor normally initiates some asynchronous work, and then, once that completes, either calls the resolve function to resolve the promise or else rejects it if an error occurred.
Request takes a callback which gets called when the API call has returned. In this callback, we first check for an error. If there’s no error, we call the resolve function passed to the executor function used to create the promise. Similarly, in case of an error, we call the reject function.
let requests = ids.map(id => {
return new Promise((resolve, reject) => {
request({
uri: <API url>+'?id=' + id,
method: 'GET'
})
},
(err, res, body) => {
if (err) { reject(err) }
resolve(body)
})
)
})
2. The code to be executed when the promise returned by Promise.all() has resolved
The promise returned by Promise.all() is resolved/rejected asynchronously (as soon as the stack is empty) when all the promises in the given iterable have resolved
Promise.all(requests)
.then(body => {
body.forEach(res => {
if (res)
productsToReturn.push(JSON.parse(res).productInfo)
})
})
Here we receive the responses received from each of our API calls in an array. We manipulate each of the responses as per our requirements and push it into an array.
Here’s the full code
var productsToReturn = []
let requests = ids.map(id => {
//create a promise for each API call
return new Promise((resolve, reject) => {
request({
uri: <API url>+‘?id=' + id,
method: ‘GET’
},
(err, res, body) => {
if (err) { reject(err) }
//call the resolve function which is passed to the executor //function passed to the promise
resolve(body)
})
})
})
Promise.all(requests).then((body) => {
//this gets called when all the promises have resolved/rejected.
body.forEach(res => {
if (res)
productsToReturn.push(JSON.parse(res).productInfo)
})
}).catch(err => console.log(err))
Looks good, however, it’s not a good practice, cause you have a limit of open connections that’s is around 6 in modern browsers, a better approach is to bundle the items on the backend, and fetch all in one call…
How about AWS Lambda? As far as I can tell, this is a great use case for it, however, I am still doing more research. If I could use this with HTTP API, not REST…
For the sake of context, I am doing something like this(in nodejs):
https://disqus.com/by/disqus_VwZInIk5Hd/