New to Node.js here. I'm looking for the correct way to make N asynchronous API calls from within another function, and combining their results to use further downstream. In my case, N would be reasonably small and blocking for their execution not too bad.
In synchronous execution the implementation for combine() below should work.
If I only needed the results from one API call it would be straightforward to implement the following logic in a callback function supplied to callAPI(). Where I stumble is when I need all the results combined before before executing foo(total, [...args]).
I looked into async.whilst but wasn't able to get that to work. I'm skeptical if that actually is the correct fit to my needs. I've also looked into Promises which seems to be the correct lead but it would be nice to get reassurances before crawling into that cavernous rabbit hole. Be it that Promises is the correct way, which module is the standard to use in Node.js projects?
var http = require('http');
function callAPI(id) {
var options = {
host: 'example.com',
path: '/q/result/'.concat(id)
}
var req = http.get(options, (res) => {
var body = [];
res.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
return body;
}).on('error', (err) => {
console.error(err);
});
});
}
function combine(inputs) {
var total = 0;
for (i=0; i < inputs.length; i++) {
total += callAPI(inputs[i]['id']);
};
console.log(total);
// call some function, foo(total, [...args])
}
Edit 1:
I attempted to follow samanime's answer below and modify the API call to return a Promise. See:
function callAPI(id) {
return Promise((resolve, reject) => {
var options = {
host: 'example.com',
path: '/q/result/'.concat(id)
}
var req = http.get(options, (res) => {
var body = [];
res.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
resolve(body);
}).on('error', (err) => {
reject(err);
});
});
});
}
function combine(inputs) {
var combined = [];
for (i=0; i < inputs.length; i++) {
total += callAPI(inputs[i]['id']);
.then(result => {
combined.push(result);
});
};
var total = combined.reduce((a, b) => a + b, 0);
console.log(total);
// call some function, foo(total, [...args])
}
This seems to get me halfway there. If I console.log(combined) inside the then() block I can see the list building up with results from the API calls. However, I still can't access the complete combined at the "end" of the for loop. Can I attach a callback to something to run after the full list has been built? Is there a better way?
Edit 2 (My solution - per Patrick Roberts suggestion)
function callAPI(id) {
return Promise((resolve, reject) => {
var options = {
host: 'example.com',
path: '/q/result/'.concat(id)
}
var req = http.get(options, (res) => {
var body = [];
res.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = parseInt(Buffer.concat(body));
resolve(body);
}).on('error', (err) => {
reject(err);
});
});
});
}
function combine(inputs) {
var combined = [];
Promise.all(inputs.map(input => callAPI(input.id)))
.then((combined) => {
var total = combined.reduce((a, b) => a + b, 0);
// foo(total, [...args])
});
};