wait for result from an async method - node.js

I'm trying to call an async method from for loop, but it doesn't wait for the result from that method.
Below is my code:
async function fetchActivityHandler (req, reply) {
esClient.search({
index: 'user_activity',
type: 'document',
body: {
_source : ["userId","appId","activity","createdAt","updatedAt"],
query: {
bool : {
must:[
{match : { 'userId': req.params.id }}
]
}
}
}
},async function (error, response, status) {
if (error){
console.log('search error: '+error)
}
else {
var activities = [];
//await Promise.all(response.hits.hits.map(async function(hit){
for (const hit of response.hits.hits) {
var activity = hit._source
var app = await fetchAppDetails(activity.appId);
console.log(app);
activity = {...activity,app : app}
activities.push(activity);
console.log(activity);
}
reply.status(200).send(activities);
}
});
}
async function fetchAppDetails (appId) {
esClient.get({
index: 'app',
type: 'document',
id: appId
}, function (err, response) {
console.log(response._source);
return (response._source);
});
}
What may be the problem. I'm using async and await, but it is not working.

Await works with promise. You should wrap your function with promise to get this work. Hope this will help you. Also you do not need to use async on fetchActivityHandler function. Only in the callback which you have already used.
function fetchAppDetails (appId) {
return new Promise((resolve,reject)=>{
esClient.get({
index: 'app',
type: 'document',
id: appId
}, function (err, response) {
if(err){
reject(err);
}
else{
resolve(response)
}
});
});
}

Related

why does my nodejs function returning undefined?

function getUserByStudentId(NIM) {
db.query('SELECT * FROM data_admin WHERE id_mahasiswa = ?', [NIM], async (err, result) => {
if (!result) {
return null
} else {
var data = await {
id: result[0].id_Admin,
email: result[0].email,
jabatan: result[0].jabatan,
password: result[0].password,
id_mahasiswa: result[0].id_mahasiswa,
id_Acara: result[0].id_Acara,
id_Organisasi: result[0].id_Organisasi
}
console.log(data) // there is a value here
return data
}
})
}
console.log(getUserByStudentId('1301194051')) // undefined returned
I'm a student and start learning nodejs. Would you explain to me, why my function returning undefined
console.log(getUserByStudentId('1301194051')) // undefined
but when I console.log on the function I got returned value
I'll promisify the function for you:
function getUserByStudentId(NIM) {
return new Promise(function(resolve, reject) => {
db.query('SELECT * FROM data_admin WHERE id_mahasiswa = ?', [NIM], (err, result) => {
if (!result) {
resolve(null);
} else {
var data = {
id: result[0].id_Admin,
email: result[0].email,
jabatan: result[0].jabatan,
password: result[0].password,
id_mahasiswa: result[0].id_mahasiswa,
id_Acara: result[0].id_Acara,
id_Organisasi: result[0].id_Organisasi
}
console.log(data) // there is a value here
resolve(data);
}
});
});
}
If you're going to use this function in global scope, use then:
getUserByStudentId('1301194051').then(result => {
console.log(result);
});
If you want to use this function inside an async function, you can await the result:
async function doSomethingWithUser(NIM) {
const user = await getUserByStudentId(NIM);
}
For example, if you're using express:
app.get('/user/:id', async (res, req) => {
const NIM = req.param.id;
const user = await getUserByStudentId(NIM);
res.json({ user });
});

How to handle async when making mongoose query in each array element in express?

On the following post method, I'm having some issues due to moongose async. res.send(suggestions) is executed first then Expense.findOne.exec
app.post('/suggestions', async function(req, res) {
const suggestions = await req.body.map((description) => {
Expense.findOne({ description: new RegExp(description, 'i') }).exec((err, result) => {
if (result) {
console.log(result.newDescription);
return {
description,
newDescription: result.newDescription,
category: result.category,
subcategory: result.subcategory
};
}
});
});
res.send(suggestions);
});
The result is a array of null values. How can I executed a query for each item, then execute res.send(suggestion)?
Found solution with the following code:
app.post('/suggestions', async function(req, res) {
try {
if (req.body.length > 0) {
const suggestions = req.body.map((description) =>
Expense.findOne({ description: new RegExp(description, 'i') })
);
const results = await Promise.all(suggestions);
return res.send(results);
}
} catch (e) {
console.log('error', e);
}
});

Return not stopping function

I have the following code
insertcontact(root, args) {
var finalresult = '';
var soap = require('soap');
var url = 'http://192.168.100.2/setlead/webservice.asmx?wsdl';
var soapargs = {
contacto: args.contacto, token: args.token, PrimeiroNome: args.PrimeiroNome, Apelidos: args.Apelidos, Email: args.Email, Telefone: args.Telefone, Origem: args.Origem
};
soap.createClient(url, function (err, client) {
if (err) {
console.log(err);
finalresult = err
return { contacto: "error cliente" };
}
else {
client.OperationDetail(args, function (err, result) {
console.log(result);
return { token: result };
});
}
});
return {
contacto: args.contacto,
PrimeiroNome: args.PrimeiroNome,
token: args.token,
Apelidos: args.Apelidos,
Email: args.Email,
Telefone: args.Telefone,
Origem: args.Origem
};
}
}
The operation does not trigger any error and I do receive the result in the console log. But I don't receive the return declared right after that part. The function goes on and I receive the last return declared. Shouldn't it stop at the return result?
This is a classic mistake in Javascript.
The function will return even before starting to create the soap client, because of its async behavior.
That function implementation is not very good as it returns the params whatever the result of the client creation process.
It would be much better to do it like this, with embeded soap args in the return statement if needed, and of course without the last return statement:
// ...
soap.createClient(url, function (err, client) {
if (err) {
console.log(err);
finalresult = err
return { contacto: "error cliente" };
} else {
client.OperationDetail(args, function (err, result) {
console.log(result);
return {
token: result,
soapargs: soapargs
};
});
}
});
wrap your returned object in parenthesis like this
return ({
contacto: args.contacto,
PrimeiroNome: args.PrimeiroNome,
token: args.token,
Apelidos: args.Apelidos,
Email: args.Email,
Telefone: args.Telefone,
Origem: args.Origem
});

Node async callback was already called when trying to make a nested query

I am getting a Callback was already called error while trying to make asynchronous queries using the MEAN stack. I need the second callback to only trigger after the nested query has been completed (as per the comments in the code). Why am I getting this error?
Example of the route:
router.route('/teams/:user_id').get(function (req, res) {
TeamProfile.find({
Members : {
$in : [req.params.user_id]
}
}).exec(function (err, teamProfiles) {
var asyncTasks = [];
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
UserProfile.find({
UserID : {
$in : teamProfile.Members.map(function (id) {
return id;
})
}
}, function (err, userProfiles) {
teamProfile.Members = userProfiles;
callback();
})
});
});
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
Draft.find({
_id : {
$in : teamProfile.Drafts.map(function (id) {
return id;
})
}
}, function (err, drafts) {
teamProfile.Drafts = drafts;
drafts.forEach(function (draft) {
Comment.find({
_id : {
$in : draft.Comments.map(function (id) {
return id;
})
}
}).exec(function (err, comments) {
draft.Comments = comments;
callback();
//This is where the callback should be called
//Throws Error: Callback was already called.
})
})
})
});
});
async.parallel(asyncTasks, function () {
res.json(teamProfiles)
});
});
})
I am using async.parallel() to perform all the queries. I am very new to all of this so please excuse some beginner mistakes.
You are iterating over drafts synchronously and calling async's callback function on the first item. Getting an error when you try to call it again with the second item is expected behaviour.
You should instead call the done callback once you have finished all the draft queries, not for each one. Since you are using async, you could nest another async.each to handle this:
router.route('/teams/:user_id').get(function (req, res) {
TeamProfile.find({
Members : {
$in : [req.params.user_id]
}
}).exec(function (err, teamProfiles) {
var asyncTasks = [];
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
UserProfile.find({
UserID : {
$in : teamProfile.Members.map(function (id) {
return id;
})
}
}, function (err, userProfiles) {
teamProfile.Members = userProfiles;
callback();
});
});
});
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
Draft.find({
_id : {
$in : teamProfile.Drafts.map(function (id) {
return id;
})
}
}, function (err, drafts) {
teamProfile.Drafts = drafts;
async.each(drafts, function(draft, draftCallback){
Comment.find({
_id : {
$in : draft.Comments.map(function (id) {
return id;
})
}
}).exec(function (err, comments) {
draft.Comments = comments;
draftCallback();
});
}, function(err){
callback();
});
});
});
});
async.parallel(asyncTasks, function () {
res.json(teamProfiles)
});
});
});

Added property don't get encoded with JSON

I am trying to get all course from the database and then add course_has_users if it exist.
The code works until I try to JSON encode it. Then I lose course_has_users when my angular front-end receives it.
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
}).then(function (courses) {
async.each(courses, function (course, callback) {
Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {}).then(function (course_has_user) {
course.course_has_users = course_has_user;
callback();
})
}, function (err) {
onSuccess(courses);
});
});
Route
.get(function (req, res) {
var course = Course.build();
course.retrieveAll(req.user, function (courses) {
if (courses) {
res.json(courses);
} else {
res.status(401).send("Courses not found");
}
}, function (error) {
res.send("Courses not found");
});
})
async.each will just iterate through it.
Use async.map and return course after setting course has users on it.
It should just work then. ;)
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
}).then(function (courses) {
async.map(courses, function (course, callback) {
Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {}).then(function (course_has_user) {
course.course_has_users = course_has_user;
callback(null, course);
})
}, function (err, _courses) {
// Note that we use the results passed back by async here!
onSuccess(_courses);
});
});
So you could also do, to simplify things a bit
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
})
.map(function (course) {
return Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {})
.then(function (course_has_user) {
course.course_has_users = course_has_user;
return course;
})
})
.then(onSuccess);
});
The problem was with Sequelize and fixed the issue by changing it's toJSON method and using async.map
Haven't tested with async.each but should work without map
instanceMethods: {
toJSON: function () {
var json = this.values;
json.course_has_users = this.course_has_users;
return json;
},
};
Retrieve method
retrieveMyCourses: function (user, onSuccess, onError) {
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
}).
then(function (courses) {
async.map(courses, function (course, callback) {
Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {}).then(function (course_has_user) {
course.course_has_users = course_has_user;
callback(null, course);
})
}, function (err, _courses) {
var test = JSON.parse(JSON.stringify(_courses));
onSuccess(_courses);
});
});
},
Route
router.route('/api/myCourses')
// Get all courses
.get(function (req, res) {
var course = Course.build();
course.retrieveMyCourses(req.user, function (courses) {
if (courses) {
res.json(courses);
} else {
res.status(401).send("Courses not found");
}
}, function (error) {
res.send("Courses not found");
});
});
Sequelize issue:
https://github.com/sequelize/sequelize/issues/549

Resources