I am new to nodejs and async. Having trouble understanding how can I wrap the two separate async.each methods to have one res.render...I am trying to display a list of valid account ids, and valid user ids on the front end.
The two separate async.each methods are:
async.each(account_ids, function(accountId, callback) {
console.log('Processing accountId ' + accountId);
callingExternalApi(accountId, callback, function(err, response){
if(err){
console.log("error account");
}
console.log("account response is: ", response);
});
}, function(err) {
if( err ) {
console.log('An account failed to process');
} else {
console.log('All accounts have been processed successfully');
}
});
and
async.each(email_ids, function(emailId, callback) {
console.log('Processing email id ' + emailId);
request({
url: emailIdlookupUrl,
method: 'POST',
json: {
email_address: emailId
}
}, function (err, response, body) {
if (err) {
logger.error(err);
req.flash('error', err.message);
return res.redirect('?');
}
if (response.statusCode !== 200) {
const msg = 'Unable to verify user';
req.flash('error', msg);
return res.redirect('?');
}
console.log("user id is: ", body.user.id);
callback();
});
}, function(err) {
if( err ) {
console.log('An email failed to process');
} else {
console.log('All user emails have been processed successfully');
}
});
Any help is highly appreciated. Please excuse me for any redundant callbacks or error logging. Still learning nodejs.
Thanks!!
The main issue is not that you are invoking both of these async.each calls. The problem is that they will run in parallel, and the fastest one to invoke req.* functions or callback function will return a response to the connection.
Both of these functions return promises if their callback parameters are omitted.
I recommend reading up on both the async library and JS async/await in general:
https://javascript.info/async-await
https://caolan.github.io/async/v3/docs.html#each
https://zellwk.com/blog/async-await-express/
Note that async also accepts native async functions, which many finder cleaner and easier to understand.
Here is what I think you want from the code above, including compiling the results into lists:
var request = require("request-promise");
async function checkAccounts(account_ids) {
const valid_accounts = [];
await async.each(account_ids, async function(accountId) {
console.log("Processing accountId " + accountId);
const extAPIresult = await callingExternalApi(accountId);
console.log("account response is: ", extAPIresult);
});
valid_accounts.push(extAPIresult);
console.log("All accounts have been processed successfully");
return valid_accounts;
}
async function checkEmails(email_ids) {
const valid_emails = [];
await async.each(email_ids, async function(emailId) {
console.log("Processing email id " + emailId);
const reqresult = await request({
url: emailIdlookupUrl,
method: "POST",
json: {
email_address: emailId
}
});
if (reqresult.statusCode !== 200) {
throw new Error("Unable to verify user");
}
valid_emails.push(reqresult.body.user.id);
console.log("user id is: ", reqresult.body.user.id);
});
console.log("All emails have been processed successfully");
return valid_emails;
}
async function doChecks() {
const accounts = checkAccounts(account_ids);
const emails = checkEmails(email_ids);
const responses = await Promises.all([accounts, emails]);
console.log("All checks have been processed successfully");
return responses;
}
function get(req, res) {
doChecks()
.then(responses => {
res.send("All checks have been processed successfully");
res.send(String(responses));
})
.catch(err => {
req.flash("error", err.message);
res.redirect("?");
});
}
Related
The function gets resolved on its own and no data is returned from the function but null.
Can anyone solve this for me please?
print is happening like below :
[ RowDataPacket { payment_id: 'PAYID-MDRPLBY9LS10853614735924' } ]
data not 000000
data not 1111111
promise2
Get Payment Response
Payment Object
but it should be :
[ RowDataPacket { payment_id: 'PAYID-MDRPLBY9LS10853614735924' } ]
data not 000000
data not 1111111
Get Payment Response
Payment Object
promise2
async function func(req, res) {
return new Promise((resolve, reject) => {
let email_id = req.body.email_id;
let arr = [];
let ctr = 0;
sql_get_orderid =
"Select payment_id from paypal.execute_order where email_id=?";
db.query(sql_get_orderid, [email_id]).then(function (data) {
console.log(data);
if (data.length != 0) {
console.log("data not 000000");
data.forEach((x) => {
console.log("data not 1111111");
var paymentId = x.payment_id;
paypal.payment.get(paymentId, function (error, payment) {
if (error) {
console.log(error);
reject({ auth: true, message: "Error on the Server" });
} else {
console.log("Get Payment Response");
console.log(JSON.stringify(payment));
arr[ctr] = payment;
ctr = ctr + 1;
// resolve(payment);
// if(ctr==data.length)
}
});
});
resolve(arr);
}
});
});
}
router.post(
"/get_all_orders",
VerifyToken.Token_Verification,
async (req, res) => {
let arr = await func(req, res);
console.log("promise2");
res.send({ data: arr });
}
);
The reason why your logs are not in the order you expect it to be is that forEach in javascript expects a synchronous callback function.
It does not wait for promises or callbacks inside its callback to return.
Therfore in your code when func is invoked and once db.query is performed and if data.length is valid data not 000000 is logged.
Then iteration begins with forEach. Here for Each element in iteration data not 1111111 will be logged and an asynchronous call is made through paypal.paymeny.get method.
As mentioned above, forEach callbacks does not wait for it to get resolved and next iteration is continued and so on.
Once iteraions are done, resolve is invoked. By the time when resolve is invoked paypal.paymeny.get has not yet returned and thus empty array will be passed.
Then when the control is passed to the route handler promise2 is logged.
Get Payment Response is logged later once paypal.payment.get returns.
To avoid this, the execution sequence can be controlled with the help of promises. Please try with the below code.
async function func(req, res) {
try {
const email_id = req.body.email_id;
const sql_get_orderid =
"Select payment_id from paypal.execute_order where email_id=?";
const data = await db.query(sql_get_orderid, [email_id]);
if (!data.length)
return;
console.log('data not 000000');
const getPaymentPromises = data.map(x => new Promise((resolve, reject) => {
console.log("data not 1111111");
paypal.payment.get(x.payment_id, (error, payment) => {
if (error)
return reject(error);
console.log("Get Payment Response");
console.log(JSON.stringify(payment));
resolve(payment);
});
}));
return await Promise.all(getPaymentPromises);
} catch (error) {
return { auth: true, message: 'Error on the Server' };
}
}
I'm refactoring my code to remove a "callback hell" using Promises, but encountered an error that I cannot pass. My code receives list of IDs and processes them making few database calls, that is why I had this "callback hell".
Everything worked fine until Promises. The res is equal 0 when I had to respond back to the client.
function processVMDelete(returnedVMIDs){
return new Promise((resolve, reject) => {
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlUpdateDELETE_STATE_ByVMID, [
'DELETE',
returnedVMIDs
], function(err, rows) {
if (err){
reject(err);
}
console.log('finish update');
// dont' need to return anything here
resolve(0);
});
mariasqlClient.end();
});
}
function getListExpVM(){
return new Promise((resolve, reject) => {
var vmList = [];
var mariasqlClient = dbConnection();
mariasqlClient.query( sqlSearch_ByUSERNAMEAndSTATE, [
requesterUsername,
'ACTIVE'
], function(err, rows) {
if (err){
reject(err);
}
vmList = filterExpiredVMs(rows);
var response = {
status : 200,
success : 'Successfull',
data : vmList,
requester: requesterUsername
};
resolve(response);
});
mariasqlClient.end();
});
}
router.post('/processVMs', function(req, res) {
var returnedVMIDs = JSON.parse(req.body.data);
processVMDelete(returnedVMIDs)
.then(res => {
console.log('done');
// check if there is more available for the user:
getListExpVM()
.then(response => {
console.log('sending back list of VMs');
//===>>> ERROR HERE: res.end is not a function
res.end(JSON.stringify(response));
})
.catch(err => {
console.log('error', err.message);
logger.error("Error getting expired VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
})
.catch(err => {
console.log('error', err.message);
logger.error("Error processing VMs: " + err.message);
//===>>> ERROR HERE: res.send is not a function
res.status(500).send({error: err.message})
});
});
You've redefined res with this:
processVMDelete(returnedVMIDs)
.then(res => {...})
This will hide the higher scoped res associated with the overall request (the one you need to use for res.end()). Change the name of this one to something else like result and then change the corresponding references that use this result.
I am trying to send messages to mobile numbers using message bird API, i have completed the coding part but its not sending the messages, even i can see i have sent messages and being delivered in my message bird profile. I came to know that this issue can be due to not using async and await. Here is my code,
api.get('/sendmessage', function (req, res) {
var id = req.headers.authorization;
if (req.session.id) {
var userid = req.session.user[0].id;
var username = userInfo.find({ id: userid }, function (err, user) {
if (err) { res.send("New Error: " + err); }
else {
if (!_.isEmpty(user)) {
var contact = user[0].contact;
var messagebird = require('messagebird')('ACCESS_KEY_API'); // Api for testing
var params = {
'originator': 'MessageBird',
'recipients': [
contact
],
'body': 'Hello, world!'
};
messagebird.messages.create(params, function (err, response) {
if (err) {
return console.log("Error sent to Console: " + err);
}
else {
console.log(response);
return res.send(response);
}
});
}
else {
res.send("No Results");
}
}
});
}
});
I send data from my input fields to my api:
$.ajax({
url: '/api/login',
type: 'GET',
dataType: 'json',
ContentType: 'application/json',
data: {formData},
success: (data) => {
console.log('SUCCESS')
console.log(data)
this.setState({
isInProcess: false
})
},
error: (jqXHR) => {
console.log(jqXHR)
console.log('ERROR')
this.setState({isInProcess: false})
}
})
on my server-side I have a function to see if I have required user in db:
async function findUser(data) {
try {
const user = await User.findOne({email: data.email,
password: data.password})
console.log('User was found')
return { user }
} catch (err) {
console.log('error', err)
throw err
}
}
which will be executed here:
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
return res.json(foundUserData)
} catch (err) {
return res.status(400).json(err)
}
})
It works fine, but if a user wasn't found in db i sends success anyway. Why?
await findUser(data.formData) won't throw error, return either null or user object. You may check something following
app.get('/api/login', async (req, res) => {
const data = req.query
try {
const foundUserData = await findUser(data.formData)
if(foundUserData && foundUserData.user) {
return res.json(foundUserData)
} else {
return res.status(400).json({message: 'User not found'});
}
} catch (err) {
return res.status(500).json(err)
}
})
It sends success because none of your queries error'ed, just because it didn't find anything does not mean that the query failed because it obviously succeeded in finding out if what ever you're looking for exists or not.
To send an error in case of not found you need to check if response is empty in which case you want to send error
When no user is find you get a null value. You may try to put more logic on your success parameter with that for example:
success: function (data) {
if(!!data && data != null) {
alert('Success');
} else {
alert('No data');
}
}
I have an array of items that I need to post to my server. I've tried the following but i never iterates.
var i = 0;
while (i < numOfItems) {
var item = items[i];
var a;
for(var ik in item){
console.log(item[ik]);
a = item[ik]; // Gets the key
break;
}
var formData = {
ID : ID,
UID : UID,
item : a
}
request.post({url:'http://example.com/a', formData: formData}, function(err, httpResponse, body){
if (err) {
return console.error('Post failed:', err);
}
console.log('Post successful! Server responded with:', body);
i++;
});
}
Your code won't work because request.post is asynchronous. If your objective is to make a call for each element in the array, a working and a more elegant solution would be to use Promises.all().
Here's your code modified with Promises —
function postRequest(url, formData) {
return new Promise((resolve, reject) => {
request.post({ url, formData }, function (err, httpResponse, body) {
if (!error) {
resolve({ message: 'Post successful!', response: body });
} else {
reject(err);
}
});
})
}
// Map your data array to an array of Promises
let promises = yourArray.map(element => {
let formData = {
ID: ID,
UID: UID,
item: element
}
return postRequest({ url: 'http://example.com/a', formData: formData })
});
// Wait for all Promises to complete
Promise.all(promises)
.then(results => {
// Handle results
})
.catch(e => {
// Handle error
});
A few things to note -
I'm reusing the fields ID and UID as-is, as it isn't clear where they come from in your code.
Replace yourArray with the array containing your data items.