Node.js For Loop async request - node.js

I have for loop with a request (and another request in callback).
I have problem with memory usage (when i am go through a lot of request at same time)
here is the sample code :
var request = require('request');
for(var j=1;j<=10;j++){
request({
method: 'GET',
url: 'https://api.domain.com/items/?page='+j+'&limit=1000',
headers: {
'Content-Type': 'application/json'
}}, function (error, response, body) {
var data = JSON.parse(body)
for(var i=0;i<data.length;i++){
request({
method: 'GET',
url: 'https://api.domain.com/itemDetail/'+data[i].id,
headers: {
'Content-Type': 'application/json',
}}, function (error, response, body) {
var itemDetails = JSON.parse(body);
// save items to mongodb
}
}
});
}
The solution suggested to me it was using async module.
After reading documentation i find out eachlimit suit my needs.
The problem i have its i can use eachlimit for second loop but for first loop i dont know how use it (because first loop in not an array).
here code so far i get , and its not working:
var request = require('request');
var mongodb = require('mongodb');
var async = require('async');
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/subtitle';
for(var j=1;j<=10;j++){
request({
method: 'GET',
url: 'https://api-v2.domain.com/news/popular?page='+j+'&limit=1000',
headers: {
'Content-Type': 'application/json',
'api-version': '2',
'api-key': 'my-api-key'
}}, function (error, response, body) {
try {
var data = JSON.parse(body);
} catch (e) {
// An error has occured, handle it, by e.g. logging it
console.log(e);
}
async.each(data, function(item, callback) {
request({
method: 'GET',
url: 'https://api-v2.domain.com/news/'+item.id,
headers: {
'Content-Type': 'application/json',
'api-version': '2',
'api-key': 'my-api-key'
}}, function (error, response, body) {
// sava item in mongodb
})
callback();
}, function(err){
if( err ) {
console.log('A item failed to process');
} else {
console.log('All items have been saved successfully');
}
});
})
}

You can achieve this in a number of ways, one of them would be using whilst. With your code it'd look something like this (removed error handling and request options for readability here):
var requestCount = 0;
async.whilst(
function () {
return requestCount < 10;
},
function (firstCallback) {
request({url: "https://api-v2.domain.com/news/popular?page=" + requestCount + "&limit=1000"}, function (err1, res1, body1) {
var data = JSON.parse(body1);
async.each(
data,
function(item, secondCallback) {
request({url: "https://api-v2.domain.com/news/" + item.id}, function (err2, res2, body2) {
// sava item in mongodb
secondCallback();
});
},
function(err){
requestCount++;
firstCallback();
}
);
});
},
function (err) {
//all requests done
}
);
Once you start to nest so many callbacks, you should probably refactor this and put the second loop in it's own function. For example:
var requestCount = 0;
async.whilst(
function () {
return requestCount < 10;
},
function (callback) {
request({url: "https://api-v2.domain.com/news/popular?page=" + requestCount + "&limit=1000"}, function (err, res, body) {
var items = JSON.parse(data);
getNews(items, function(newsErr){
requestCount++;
callback(newsErr);
});
});
},
function (err) {
//all requests done
}
);
//----------------------------------------------------
function getNews(items, callback){
async.each(items, function(item, itemCallback) {
request({url: "https://api-v2.domain.com/news/" + item.id}, function (err2, res2, body2) {
// sava item in mongodb
itemCallback();
});
}, callback);
}
Also make sure to call the async callbacks within the callbacks of the request (you weren't doing it so in your code).

Related

how to get data outside request

I have a scenario where i need to take response (body) of request method outside request. How can i do it?
request.post({
url: 'http://localhost/api/messages',
form: { key: message }
}, function (err, httpResponse, body) {
tmsg = body;
})
console.log(tmsg);
I need this tmsg outside for next processing, Actual scenario is as below.
app.post('/incomemsg', function (req, res) {
var mediaCount = req.body.NumMedia;
if (mediaCount == 0) {
//var twiml = getResponse(message);
var twiml = new MessagingResponse();
request.post({
url: 'http://localhost:3978/api/messages',
form: { key: message }
}, function (err, httpResponse, body) {
tmsg = body;
})
console.log(tmsg);
}else {
//dosomething which outputs tmsg
}
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(tmsg.toString());
});
The problem is you are trying to assign value to a global variable in request.post's callback() which is only called after request.post is executed by Asynchronous logic(API calls are all async), so a better way would be to promisify request.post and await the request.post to make it seem synchronous.
const requestPromisified = requestObject =>
new Promise((resolve, reject) => {
request.post(requestObject, function(err, httpResponse, body) {
if (err) {
reject(err);
}
resolve(body);
});
});
const body = await requestPromisified({
method: "POST",
url: "http://localhost/api/messages",
form: { key: message }
});
You only can do something with tmsg when you made the request so you need to rearrange your code like this:
app.post('/incomemsg', function (req, res) {
var mediaCount = req.body.NumMedia;
var twiml = new MessagingResponse();
request.post({
url: 'http://localhost:3978/api/messages',
form: { key: message }
}, function (err, httpResponse, body) {
tmsg = body;
console.log(tmsg);
if (mediaCount === 0) {
//do something with tmsg
} else {
//do something else with tmsg
}
res.writeHead(200, { 'Content-Type': 'text/xml' });
res.end(tmsg.toString());
});
});
Otherwise tmsg will be null because there was no request made to fill that variable.

How to wait for a asynchronous function to finish it's execution in nodejs [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have a function that returns a value which it gets from an http GET request in nodejs. How do I wait for the async request function to finish and return the result from the function that generates the request.
I have checked async library in npm, but that doesn't solve my problem.
Thanks for the help!!
function daemonGetNodeStatus(kubeURL, nodeName) {
console.log(nodeName);
var request = require("request");
var options = {
method: 'GET',
url: kubeURL+'/api/v1/nodes/'+nodeName+'/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
request(options, function(error, response, body) {
if (error)
throw new Error(error);
var bodyJSON = JSON.parse(body);
var result = [];
var temp = {};
for (var i = 0; i < bodyJSON.status.conditions.length; i++) {
if(bodyJSON.status.conditions[i].status == "True"){
result.push(bodyJSON.status.conditions[i].type);
}
}
console.log(result);
});
};
You could use a Promise.
function daemonGetNodeStatus(kubeURL, nodeName) {
console.log(nodeName);
var request = require("request");
var options = {
method: 'GET',
url: kubeURL+'/api/v1/nodes/'+nodeName+'/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
return new Promise((resolve, reject) => {
request(options, function(error, response, body) {
if (error)
reject(new Error(error));
var bodyJSON = JSON.parse(body);
var result = [];
var temp = {};
for (var i = 0; i < bodyJSON.status.conditions.length; i++) {
if(bodyJSON.status.conditions[i].status == "True") {
result.push(bodyJSON.status.conditions[i].type);
}
}
resolve(result);
});
}
daemonGetNodeStatus(url, name).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
function daemonGetNodeStatus(kubeURL, nodeName, callback) {
console.log(nodeName);
var request = require("request");
var options = {
method: 'GET',
url: kubeURL+'/api/v1/nodes/'+nodeName+'/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
request(options, function(error, response, body) {
if (error)
{
callback(error);
} else {
var bodyJSON = JSON.parse(body);
var result = [];
var temp = {};
for (var i = 0; i < bodyJSON.status.conditions.length; i++) {
if(bodyJSON.status.conditions[i].status == "True"){
result.push(bodyJSON.status.conditions[i].type);
}
}
callback(null, result);
}
});
}
One of the best things about Node is it's asynchronous. It might be hard to understand in the beginning, but once you know, it's the best thing. It's also the reason Node is so fast.
So, when you have to run an asynchronous function, you also send an extra function to the asynchronous function called a callback function which will be executed once the async operation is done. The above code illustrates this.
Then you can use the function like this:
daemonGetNodeStatus('http://kube.com/something', 'name', function(err, result){
if(err) {console.log(err); }
else {
console.log(result);
// do whatever you want with the async result
}
});
I'd like to answer with one more approach - async\await. If you have NodeJS v8 installed, better to stick with async\await.
Also, I made some improvements, feel free to ignore them if you don't like it.
1) async/await offers to you more convenient way to write and deal with asynchronous code. Instead of callbacks or Promise chains, you just write await.
2) Instead of iterating through array via for-loop you can use filter and map to find all the k8s conditions where status is True.
3) json field in request options will parse response as JSON and returns to you already parsed JSON value.
const request = require("request-promise");
async function daemonGetNodeStatus(kubeURL, nodeName) {
const options = {
json: true,
method: 'GET',
url: kubeURL + '/api/v1/nodes/' + nodeName + '/status',
headers: {
'Cache-Control': 'no-cache',
'Authorization': 'Bearer '+constants.accessToken
}
};
const response = await request(options);
return response.status.conditions.filter(cond => cond.status === 'True').map(cond => cond.type);
});
UPD
Any async function returns Promise, so you need to await it or call it with then\catch:
async function someAnotherAsyncFunction() {
const types = await daemonGetNodeStatus('KUBE_URL', 'NODE_NAME');
// do something with types
}
or
daemonGetNodeStatus.then(types => doSomethingWithTypes(types)).catch(error => doSomethingWithError(error));

handle multiple promises in for loop with error handling

i want to handle multiple promises,in example below kouta API returns multiple sites then i want to for loop on sites to get categories one by one.
node code:
request({
url: 'https://cbatest.kounta.com/v1/companies/20155/sites.json',
headers: header_data,
json: requestData,
method: 'GET',
}, function (err, response, body) {
var site_list = body;
//res.send(site_list);
var promises = [];
for (let i = 0; i < site_list.length; i++) {
var cat_list = new Promise(function (resolve, reject) {
var site_id = site_list[i].id;
var category_arr = [];
request({
url: 'https://cbatest.kounta.com/v1/companies/20155/sites/' + site_id + '/categories.json',
headers: header_data,
json: requestData,
method: 'GET',
}, function (err, response, body) {
var category_list = body;
resolve(category_list);
});
});
cat_list.then(function (result) {
promises.push(result);
console.log(promises);
res.send(promises)
}).catch(function (err) {
console.log(err);
})
}
});
Problem: console.log(promises); only work for last id, and res.send(promises); just work only for 1 time. What i am missing?
Thanks in advance.
Your code send result just for first promise that resolved.
It's better to use from async
And the code becomes like :
request({
url: 'https://cbatest.kounta.com/v1/companies/20155/sites.json',
headers: header_data,
json: requestData,
method: 'GET',
}, function (err, response, body) {
var site_list = body;
//res.send(site_list);
var promises = [];
async.map(site_list, function(site, callback){
request({
url: 'https://cbatest.kounta.com/v1/companies/20155/sites/' + site.id + '/categories.json',
headers: header_data,
json: requestData,
method: 'GET',
}, function (err, response, body) {
var category_list = body;
callback(null, category_list);
});
}, function(err, result){
if(err) return res.send(err);
//result is array of arrays. you can shape it like you want
res.send(result);
});
});
Here's simple solution
const companiesPromise = fetch('https://cbatest.kounta.com/v1/companies/20155/sites');
const categoriesPromise = fetch('https://cbatest.kounta.com/v1/companies/20155/sites/categories');
Promise
.all([companiesPromise,categoriesPromise])
.then(responses => {
return Promise.all(responses.map(res => res.json()))
})
.then(responses => {
console.log(responses)
})
.catch(err => {
console.error(error)
})

Nodejs how to do some logic when previous logic were done

An array[1,2,3,4]take 2 of them and send 2 requests with them;
Once completed take another 2 and create two new requests;
I know I cant do it like below because Nodejs is synchronous;
var request = require('request');
var args=[1,2,3,4];
function chunk(){...}
args = chunk(args,2); //My custom function to split array into chunks
args.forEach(function(value)
{
value.forEach(function(value_value)
{
/***********sent requests**************/
var options = {
method: 'POST',
url: 'http://dev.site/date.php',
formData: {arguments:value_value}
};
request(options, function (error, response, body)
{
if (body=='hello') {
console.log(body);
}
});
/**************************************/
});
});
please Help me
You can use request-promise and q - then chain response:
var request = require('request-promise');
var Q = require('q');
var args=[1,2,3,4];
function chunk(){...}
args = chunk(args,2); //My custom function to split array into chunks
args.forEach(function(value)
{
var result = Q();
args.forEach(function (t) {
result = result.then(request_each.bind(null, value));
});
return result; // result has the final promise (use .then on that)
});
function request_each(value) {
return Q.all(value.forEach(function(value_value)
{
/***********sent requests**************/
var options = {
method: 'POST',
url: 'http://dev.site/date.php',
formData: {arguments:value_value}
};
return request(options);
/**************************************/
}));
}
You can just use q and request (changing the request_each function):
function request_each(value) {
return Q.all(value.forEach(function(value_value)
{
var deferred = Q.defer();
/***********sent requests**************/
var options = {
method: 'POST',
url: 'http://dev.site/date.php',
formData: {arguments:value_value}
};
request(options, function (error, response, body) {
if (body=='hello') {
deferred.resolve(body);
}
});
return deferred.promise;
/**************************************/
}));
}

Need to call two apis using node,js asynchronously and aggregate the response from both the apis

The code I wrote so far is as below.
I don't need the whole response but just part of it.
var request = require('request');
var async = require('async');
var asyncTasks = [];
var install;
async.series([
function (callback) {
setTimeout(function () {
request('URL', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body); // Show the HTML for the Google homepage.
}
});
}, 5000);
},
function (callback) {
setTimeout(function () {
request('URL', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body); // Show the HTML for the Google homepage.
}
});
}, 5000);
}
],
function (error, results) {
console.log(results);
});
One approach to do the above concurrently would be to use async.parallel - of the form of:
async.parallel([
function(){ ... },
function(){ ... }
], callback);
Another approach is to use a Promises library - BlueBird or Q are good choices.
Q.All is of the form of:
return Q.all([
promise1,
promise2
]).spread(function (resultFromPromise1, resultFromPromise2) {
// do something with the results...
});
You could use one of these approaches to parallelise the two calls. The outputs of each will give you an array containing the results of each call respectively.
Here is a simple illustration of each approach:
Using Async.js
var async = require('async');
var task = function (cb, count) {
setTimeout(function () {
cb(null, "complete: " + count);
}, 1000);
};
async.parallel([
function (cb) {
task(cb, 'one');
},
function (cb) {
task(cb, 'two');
}
], function (err, results) {
console.log(results);
//[ 'complete: one', 'complete: two' ]
});
Using Q:
var Q = require('q');
function task1(cb, count) {
var deferred = Q.defer();
setTimeout(function () {
return deferred.resolve(cb(null, count));
}, 1000);
return deferred.promise;
}
var myCb = function (err, count) {
return "complete: " + count;
};
Q.all([task1(myCb, 'one'), task1(myCb, 'two')])
.then(function (results) {
console.log(results);
//[ 'complete: one', 'complete: two' ]
});
Let me know if you are unclear.
Promises are there to help you out in such a case.
I would prefer to use 'Q' library.
I have modified your code to use Q library
var Q = require('q');
var request = require('request');
function makeCall() {
Q.all([req1(), req2()])
.spread(function (res1, res2) {
// This block is executed once all the functions( Provided in Q.all() ) are finished its execution.
// Use responses from called functions
}, function (err) {
// Error, If any
});
}
function req1() {
var defer = Q.defer();
var url = ''; // Specify URL
var options = {
method: 'get', // Method to use
url: url
}
request(options, function (err, res, body) {
if (err) {
return defer.reject(err);
}
return defer.resolve(body);
});
return defer.promise;
}
function req2() {
var defer = Q.defer();
var url = ''; // Specify URL
var options = {
method: 'get', // Method to use
url: url
}
request(options, function (err, res, body) {
if (err) {
return defer.reject(err);
}
return defer.resolve(body);
});
return defer.promise;
}
You can find docs for Q library here : Q docs

Resources