handle multiple promises in for loop with error handling - node.js

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)
})

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 migrate to callback nodejs

I need to transform this code into clean code that uses callbacks, because this code does not allow me to use body information elsewhere.
const endpoints = [];
function getDevicesFromPartnerCloud() {
var options = {
method: 'GET',
url: 'https://database-dcda.restdb.io/rest/endpoints',
headers: {
'cache-control': 'no-cache',
'x-apikey': '*****************************'
}
};
request(options, function (error, response, body) {
var data = JSON.parse(body);
data.forEach(function(data, index) {
let endpoint = createSceneEndpoint(data._id, data.name);
endpoints.push(endpoint);
});
});
return endpoints;
}
I think the cleanest way to do it would be to use a Promise to handle the asynchronous request. One of the most important things to remember is that functions should ideally do only one thing. That way, they are easier to test, reason about, and refactor. I would pull the code that actually makes the request into a separate function and have it return the body, then have your getDevicesFromPartnerCloud call that new function, get the data back, and process it however it wants. Most importantly, this "frees" the data from being stuck in the request callback, because you're wrapping it in a promise, and resolving it when the data is available.
Something like:
const endpoints = [];
function requestDevices() {
return new Promise(function(resolve, reject) {
const options = {
method: 'GET',
url: 'https://database-dcda.restdb.io/rest/endpoints',
headers: {
'cache-control': 'no-cache',
'x-apikey': '*****************************',
},
};
request(options, function(error, response, body) {
if (error) {
reject(error)
}
resolve({ response: response, body: body });
});
});
}
async function getDevicesFromPartnerCloud() {
const devicesResponse = await requestDevices();
const data = JSON.parse(devicesResponse.body);
data.forEach(function(data, index) {
const endpoint = createSceneEndpoint(data._id, data.name);
endpoints.push(endpoint);
});
// Do whatever else you need with devicesResponse.body
return endpoints;
}
If you wanted to go more of an es6 direction, maybe something like
let endpoints;
const requestDevices = () =>
new Promise((resolve, reject) => {
request(
{
method: 'GET',
url: 'https://database-dcda.restdb.io/rest/endpoints',
headers: {
'cache-control': 'no-cache',
'x-apikey': '*****************************',
},
},
(error, response, body) => (error ? reject(error) : resolve(body)),
);
});
const getDevicesFromPartnerCloud = async () => {
try {
const body = await requestDevices();
const data = JSON.parse(body);
endpoints = data.map(({ _id, name }) =>
createSceneEndpoint(_id, name),
);
// Do whatever else you need with devicesResponse.body
// doStuff(body)
return endpoints;
} catch (e) {
console.error(e);
}
};

Callback-return value doesn't get stored in a HTTP-request (NodeJS)

I have set up a server-application with NodeJS/Express and am trying to get the following script to work so that it stores the return value of a http-request in a variable for later use.
function checkIfVerified(req, res) {
var user_id = req.user.id
var options = {
method: 'GET',
url: 'https://MYDOMAIN/api/v2/users/' + user_id + '?fields=email_verified&include_fields=true',
headers:
{
authorization: 'Bearer ' + app.locals.token
},
body:
{
},
json: true
}
function check(callback) {
request(options, function (error, response, body) {
return callback(null, body)
})
}
var email_verified = check(function (err, data) {
if (!err) {
return data
}
})
console.log(email_verified)
return email_verified
}
For some reason the variable 'email_verified' doesn't hold any value ...
Thanks for your help!
Cheers
Philip
So after #Derlins clue I found out the following solution to the question:
function check() {
return new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) {
reject(error)
} else {
resolve(body.email_verified)
}
})
})
}
res.locals.email_verified = await check()
This does the trick :-)

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));

Node.js For Loop async request

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).

Resources