Async callback on a mongodb function - javascript

I am trying to make this function make an appropriate callback. As it is written, the callback will be called twice - once for the synchronous 'if' statement and one for the asynchronous 'test2.save' statement. I am putting the counter code in just as an example that I tried. It doesn't work since the bottom if statement is synchronous. I already know what is wrong with this code, but I have no idea about how to make it better.
var postMatches = function(user1, userResults, callback) {
User.find({}, {username: 1, testResults: 1}, (err, users) => {
var counter = 0;
users.forEach(function(user2){
counter++;
if(user1 !== user2.username && user2.testResults !== undefined) {
var test1 = new Test({
username: user1,
match: user2.username,
compatability: mbti[userResults][user2.testResults],
alreadyMatches: false
});
test1.save( () => {
var test2 = new Test({
username: user2.username,
match: user1,
compatability: mbti[user2.testResults][userResults],
alreadyMatches: false
});
test2.save( () => {
if(counter === users.length) {
callback();
}
});
})
} else {
if(counter === users.length) {
callback();
}
}
})
})
};

From the comments and questions, compiled a code here. Use async module and forEach function to iterate over users list and return callback once done. Read about async and forEach. Let me know if this works for your use case.
var async = require('async')
var postMatches = function(user1, userResults, callback) {
User.find({}, {username: 1, testResults: 1}, (err, users) => {
var counter = 0;
async.forEach(users,function(user2,iterate_callback){
if(user1 !== user2.username && user2.testResults !== undefined) {
var test1 = new Test({
username: user1,
match: user2.username,
compatability: mbti[userResults][user2.testResults],
alreadyMatches: false
});
test1.save( () => {
var test2 = new Test({
username: user2.username,
match: user1,
compatability: mbti[user2.testResults][userResults],
alreadyMatches: false
});
test2.save( () => {
iterate_callback();
});
})
} else {
iterate_callback();
}
},function(err){
console.log("Done iterating");
return callback();
});
})
};

Related

Javascript rerun the function until condition is false

Via API, I would like to list out the whole users. Each time can only have maximum 100 items for each page. So that I need to get the next_page url to rerun the function - runPages to collect the list. So the condition is when the next_page == null, then will stop the function.
In my code, I can get the next_page url. However, it doesn't run further. Can someone figure out what is the problem?
const runPages = async (values) => {
if (values.next_page != null) {
for (const field of values.results) {
row.appendChild(addCell(field.name));
row.appendChild(addCell(field.email));
tblbody.appendChild(row);
}
values = await checkPages(values.next_page); // get new values.data by url
runPages(values);
}
};
runPages(values);
const checkPages = async (value) => {
return new Promise((resolve, reject) => {
const getNewPageFromApi = async () => {
const GetUrl = `${value}`;
const Doorkey = { username: "XXX", password: "*****" };
try {
const response = await Axios.get(GetUrl, { auth: Doorkey });
if (response.data.next_page != null) {
resolve(response.data);
}
} catch (err) {
reject("no more data");
}
};
getNewPageFromApi();
});
};
I don't know if this is the answer you're looking for but since you're checking for values.next_page != null in the runPages function you can call the resolve(response.data) directly in the Promise inside checkPages function without checking for response.data.next_page != null.

How to add async/await to my functions in nodejs?

I tried to make the code asynchronous but I couldn't. What i need to do?
This is my functions:
1.
router.post('/urls', (req, response) => {
count = 2;
webUrl = req.body.url;
depth = req.body.depth;
letstart(webUrl, response);
});
function letstart(urlLink, response) {
request(urlLink, function (error, res, body) {
console.error('error:', error); // Print the error if one occurred
console.log('statusCode:', res && res.statusCode); // Print the response status code if a response was received
//console.log('body:', body); // Print the HTML for the Google homepage.
if (!error) {
getLinks(body);
if (!ifFinishAll) {
GetinsideLinks(linkslinst, response);
}
else {
console.log("Finish crawl");
}
}
else {
console.log("sorry");
return "sorry";
}
});
}
function GetinsideLinks(list, response) {
count++;
if (count <= depth) {
for (let i = 0; i < list.length; i++) {
const link = list[i].toString();
var includeUrl = link.includes(webUrl);
if (!includeUrl) {
request(link, function (error, res, body) {
console.error('error2:', error); // Print the error if one occurred
console.log('statusCode2:', res && res.statusCode); // Print the response status code if a response was received
if (!error) {
getLinks(body);
}
else {
console.log("sorry2");
}
});
}
}
ifFinishAll = true;
}
else {
console.log("finish");
ifFinishAll = true;
response.status(200).send(resArray);
};
return resArray;
}
function getLinks(body) {
const html = body;
const $ = cheerio.load(html);
const linkObjects = $('a');
const links = [];
linkObjects.each((index, element) => {
countLinks = linkObjects.length;
var strHref = $(element).attr('href');
var strText = $(element).text();
var existUrl = linkslinst.includes(strHref);
var existText = textslist.includes(strText);
if (strText !== '' && strText !== "" && strText !== null && strHref !== '' && strHref !== "" && strHref !== null && strHref !== undefined && !existUrl && !existText) {
var tel = strHref.startsWith("tel");
var mail = strHref.startsWith("mailto");
var linkInStart = isUrlValid(strHref);
if (!tel && !mail) {
if (linkInStart) {
links.push({
text: $(element).text(), // get the text
href: $(element).attr('href'), // get the href attribute
});
linkslinst.push($(element).attr('href'));
textslist.push($(element).text());
}
else {
links.push({
text: $(element).text(), // get the text
href: webUrl.toString() + $(element).attr('href'), // get the href attribute
});
linkslinst.push(webUrl.toString() + $(element).attr('href'))
textslist.push($(element).text());
}
}
}
});
const result = [];
const map = new Map();
for (const item of links) {
if (!map.has(item.text)) {
map.set(item.text, true); // set any value to Map
result.push({
text: item.text,
href: item.href
});
}
}
if (result.length > 0) {
resArray.push({ list: result, depth: count - 1 });
}
console.log('res', resArray);
return resArray;
}
I want to return/response finally to the "resArray". I tried to add async and await to function number 1 and number 2 but it didn't succeed. Maybe I need to add async/await to all functions? How can I fix that?
You can achieve your goal by using async-await.
An async function is a function declared with the async keyword, and the await keyword is permitted within them. The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.
Basic example:
function resolveImmediately() {
return new Promise(resolve => {
resolve(true);
});
}
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveImmediately();
console.log(result);
if(result) {
const anotherResult = await resolveAfter2Seconds();
console.log(anotherResult);
}
}
asyncCall();
Note: Your code is too long to debug. As a result, to make you understand about the approach (what & how to do), i have added a simple example into my answer.

function inside function is not waiting for promise in javascript

Sorry if my title is not very explicit I dont know how to explain this properly.
I am trying to use the distinct function for my app that uses loopback 3 and mongodb. It seems to work right but my endpoint wont hit the return inside my function.
This is my code
const distinctUsers = await sellerCollection.distinct('userId',{
hostId : host.id,
eventId:{
"$ne" : eventId
}
}, async function (err, userIds) {;
if(!userIds || userIds.length ==0)
return [];
const filter = {
where:{
id: {
inq: userIds
}
}
};
console.log("should be last")
return await BPUser.find(filter);
});
console.log(distinctUsers);
console.log("wtf??");
//return [];
If I uncomment the return [] it will send the return and later it will show the should be last, so even when I dont have the return it seems to finish. It is now waiting for the response. I dont like the way my code looks so any pointer of how to make this look better I will take it.
It looks like the sellerCollection.distinct takes a callback as one of it's parameters, therefore, you cannot use async/await with a callback-style function, since it's not a promise.
I would suggest turning this call into a promise if you'd like to use async/await:
function findDistinct(hostId, eventId) {
return new Promise((resolve, reject) => {
sellerCollection.distinct(
'userId',
{ hostId, eventId: { "$ne": eventId } },
function (error, userIds) {
if (error) {
reject(error);
return;
}
if (!userIds || userIds.length === 0) {
resolve([]);
return;
}
resolve(userIds);
}
)
})
}
Then, you can use this new function with async/await like such:
async function getDistinctUsers() {
try {
const hostId = ...
const eventId = ...
const distinctUsers = await findDistinct(hostId, eventId)
if (distinctUsers.length === 0) {
return
}
const filter = {
where: {
id: { inq: userIds }
}
}
const bpUsers = await BPUser.find(filter) // assuming it's a promise
console.log(bpUsers)
} catch (error) {
// handle error
}
}

callback was already called! in loopback, in updateAll function

I'm using the loopback, here while making the update call with list of objects in array.
I get in the callback is already called!
The scene is, I have defined the callback inside the loop, and in the first loop, it is get in called actually.
I am looking for the way where
I should update all list of object in query MySQL plan call.
Inward.updateIsActiveDetails = function(data, callback) {
var id = _.map(data, 'id');
if (id.length > 0) {
_.forEach(id, id => {
console.log('id....:', id)
Inward.updateAll({id}, {
isActive: 0,
}).then(updateresult => {
console.log(updateresult);
// callback(error); showing err with it... (callback already called)
}).catch(function(error) {
callback(error);
});
});
} else {
callback(null, {
success: true,
msg: 'No records to update',
});
}
};
output:
id....: 3
id....: 4
{ count: 1 }
{ count: 1 }
appreciate for right solution
The callback is supposed to be called once, you're calling it in the loop, so it will be called for each iteration of the loop. More than once. The following would be correct if for whatever reason you can't use async/await.
Inward.updateIsActiveDetails = function(data, callback) {
var id = _.map(data, 'id');
var len = id.length;
var resultList = [];
// When you call this function we add the results to our list
// If the list of updates is equal to the number of updates we had to perform, call the callback.
function updateResultList(updateResult) {
resultList.push(updateResult);
if (resultList.length === len) callback(resultList);
}
if (len > 0) {
_.forEach(id, id => {
Inward.updateAll({id}, {
isActive: 0,
})
.then(updateResult);
});
} else {
callback(null, {
success: true,
msg: 'No records to update',
});
}
};
With async/await it would be much shorter.
Inward.updateIsActiveDetails = async function(data) {
const results = [];
for(let i = 0; i < data.length; i++) {
results.push(await Inward.updateById(data[i].id));
}
return results;
}
Here is my final and working answer.
Basically, updateAll query runs once and it will run as an inbuilt query
id: {
inq: _.map(data, 'id'),
}
So, after running that it will update the respective row only! very interesting.
Inward.updateIsActiveDetails = function (data, callback) {
Inward.updateAll({
id: {
inq: _.map(data, 'id'),
},
}, {
isActive: 0,
}, function (error, resultDetails) {
if (error) {
console.log('error', error);
callback(error);
} else {
console.log('resultDetails', resultDetails);
callback(null, resultDetails);
}
});
};

Async-WaterFall not working as expected

waterfall function with two calls but the second on is not waiting for the first one to completely finish. The first one has a mongodb.find() call in it.
Here is the async-waterfall function
app.get("/news", function(req, res) {
async.waterfall([
function (callback) {
var blogs = tendigiEngine.getAllBlogs(callback);
callback(null, blogs);
},
function (blogs, callback) {
var array = tendigiEngine.seperateBlogs(blogs, callback);
callback(null, array );
}
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});
});
Here are the two functions being called:
getAllBlogs():
exports.getAllBlogs = function() {
Blog.find(function(err, theBlogs){
if(!err) {
return theBlogs;
}
else {
throw err;
}
});
}
seperateBlogs():
exports.seperateBlogs = function(blogs) {
if(blogs.length === 0 ) {
return 0;
}
else {
blogs.reverse();
var blog = blogs[0];
blogs.shift();
var finArray = [blog, blogs];
return finArray;
}
console.log("asdf");
}
It is important that seperateBlogs won't be called before getAllBlogs() has returned theBlogs, but it is being called before the value is returned. I used Async_Waterfall to avoid this problem but it keeps recurring, which means I am using it wrong. What am I doing wrong here and how can I fix it?
Thanks!
Your exported functions are missing the callback parameters:
exports.getAllBlogs = function(cb) {
Blog.find(cb);
};
exports.seperateBlogs = function(blogs, cb) {
if (blogs.length === 0 )
return cb(null, blogs);
blogs.reverse();
var blog = blogs[0];
blogs.shift();
cb(null, [blog, blogs]);
}
Then your main code can be simplified as well:
async.waterfall([
tendigiEngine.getAllBlogs,
tendigiEngine.seperateBlogs
], function (err, result) {
// result now equals 'done'
console.log("done");
console.log(result);
});

Categories

Resources