Callbacks and Async functions - javascript

How can I integrate Discovery with Conversation? I'm using NodeJS, but having problems because I want the conversation results to come after querying my data collection. I'm trying to use callbacks but no luck yet. I could use async, but could I use simple callbacks in this case? Help appreciated, thanks!
function updateMessage(res, data) {
if (!data.output) {
data.output = {};
} else {
/* THIS CODE RETURNS CONVERSATION DATA FIRST, CAUSING THE DISCOVERY QUERY
TO BECOME UNDEFINED */
if (data.context.callDiscovery === true) {
//Query collection
Discovery.query(params, function(error, results) {
data.output.text = "Getting what you need";
//Set output graph card
data.output.graph = {
title: results.title,
url: result.url,
description: results.passage_text
};
return results;
});
}
return data;
}
}

You are mixing sync and async operations.
Here is an example of a function returning synchronously:
function (options) {
if (options.flag) {
return "Flag is set";
} else {
return "Flag is not set";
}
}
This is an example of a function returning asynchronously:
function (options, done) {
Discovery.query({options}, function (err, results) {
if (err) return done(err);
return done(null, results);
});
}
However, it's not recommended to return a function accepting callback immediately based on a condition or perform an async operation and then return. If your function does not take a callback in and does not call that callback when the async operation is finished, your function will be done executing before the async operations is finished. For example:
function mixedUp(options) {
Discovery.query({options}, function (err, results) {
if (err) return err;
return results;
});
return 'default value';
}
This will always return default value, because before the Discovery request is finished, your function has returned.
You should make your function accept a callback or a done parameter, which in idiomatic node, is the last argument of your function. function (options, moreOptions, done/callback/cb) should be signature of your function. Then, your function should call that callback function when you want to perform an action.
function updateMessage(res, data, done) {
if (!data.output) {
data.output = {};
return done(null, data);
} else {
/* THIS CODE RETURNS CONVERSATION DATA FIRST, CAUSING THE DISCOVERY QUERY
TO BECOME UNDEFINED */
if (data.context.callDiscovery === true) {
//Query collection
Discovery.query(params, function(error, results) {
data.output.text = "Getting what you need";
//Set output graph card
data.output.graph = {
title: results.title,
url: result.url,
description: results.passage_text
};
return done(null, results);
});
} else {
return done(null, data);
}
}
}

Related

How to handle callbacks?

I have a function which is -:
function generateAndEmitSessionizedEvents(workflowIdVsClientId, workflowIdVsEvents, clientVsIdVsDoc, esWriteFailedWorkflowIds) {
return _generateSessionizedEvents(workflowIdVsClientId, workflowIdVsEvents, clientVsIdVsDoc, esWriteFailedWorkflowIds)
.then(function (messageInfo) {
return new Promise((resolve, reject) => {
preValidateUtil.preValidateNullCheck(messageInfo, "vo", function (messageInfo, err) {
if(err) {
logger.error("Failed to send slack notification" , err);
}
return resolve(messageInfo);
});
})
}
I have passed inside the promise preValidateNullCheck function which is -:
function notifySlack(faultyEventsList, callback) {
var text = `The data for faulty event is : ${faultyEventsList}`;
slackHelper.slack.sendNotification(text, function (err) {
if (err) {
logger.error("ERROR Failed to send the slack notification" + err);
return callback(err);
}
})
}
function preValidateNullCheck(messagesInfo, stream, callback) {
var faultyEventsList = [];
var validEventsList = [];
_.each(messagesInfo, function (messageInfo) {
var event = messageInfo.message.event_type;
findSchemaForEvent(event, messageInfo, stream, faultyEventsList, validEventsList);
});
notifySlack(faultyEventsList);
return validEventsList;
}
In notifyslack function i have handled the error and still have to handle the success callback, can't understand how to do it.
Please Note -: preValidateNullCheck function is not correct, I don't know weather i should have passed callback here too in arguments or in notifySlack function.
I can't understand how to handle callbacks as in, notifySlack must occur after everything happens in preValidateNullCheck function.
I am new to this, seeking help !!

Mongoose function logs correct data but using async function to get it returns undefined

I am trying to query some data from a database and return it to a function. The issue I am having is that when I log the results from the query it is working but when I try to query what the function is returning I am getting undefined. It could be that my async function with mongoose isn't exactly set up correctly. Or maybe I just have the wrong idea of what is happening as I am new to the idea of asynchronous programming.
async function returnBlogThumbnails(filter = "recent", callback){
console.log("returning blogs")
//For now simply filter by most recent
if(filter === "recent"){
Blog.find({}).sort('-date').exec((err,docs) => {
return docs;
});
}
}
and the function that calls this function
app.get('/', (req, res)=> {
console.log("go home");
//Call out to query the database then use async function to return
database.returnBlogThumbnails().then((blogs) => {
console.log(blogs);
//res.render('home', blogs);
});
});
As I have said the console.log spits out what I am looking for. However, the function calling the function with the query always shows undefined. Thanks
The problem is you defined your function with callback and trying to call it as a promise. Your function with promise should be:
async function returnBlogThumbnails(filter = "recent") {
return new Promise((resolve, reject) => { // creates a promise
console.log("returning blogs")
//For now simply filter by most recent
if (filter === "recent") {
Blog.find({}).sort('-date').exec((err, docs) => {
if (err) {
reject(err);
} else {
resolve(docs)
}
});
}
});
}
and then in your route you can call it as:
app.get('/', (req, res) => {
console.log("go home");
//Call out to query the database then use async function to return
database.returnBlogThumbnails()
.then((blogs) => {
console.log(blogs);
// do something with blogs
})
.catch((err) => {
console.log(err);
// handle error
});
});

async each callback is called before iteration

i have the following routing function:
router.route('/api/teamUsersWithStat/:team_id')
.get(function (req, res) {
var user_stat = academy_team_user_stat.build();
user_stat.usersByTeam(req.params.team_id, function (result) {
if (result) {
async.each(result, function () {
var i = 0;
user_stat.findModulesTaken(res.user_id, res.team_id, function (modules) {
result[i].modules = modules;
i++;
});
}, res.json(result))
} else {
res.status(401).send("Team not found");
}
}, function (error) {
res.send("Team not found");
});
});
as you can see im using the async.each method to collect additional data to my existing array.
However the res.json(result) is called without it running the actual loop.
(i can tell this as in my javascript i am debugging the response).
So what am i doing wrong?
You're calling your res.json method straight away, you're also reinitializing i inside the loop so it's always 0.
Also, each requires a callback in order to procede to the next iteration.
The following is how I'd do it:
async.each(result, function (r, callback) {
user_stat.findModulesTaken(res.user_id, res.team_id, function (modules) {
result[result.indexOf(r)].modules = modules;
callback();
});
}, function(err) {
if(err)
return res.json(err);
res.json(result);
});
res.json(result) is called as a function, and therefore invoked immediately. To make sure res.json is invoked after the async.each(), you need to pass a function as callback:
async.each(result, function () {
...
}, function(err) {
if(!err) res.json(result);
));

What's the best way to get a function return to wait until an asynchronous operation has finished?

Given the following prototype function:
Client.prototype.getLocalIp = function() {
var rtc = new window.RTCPeerConnection({iceServers: []});
rtc.createDataChannel('', {reliable: false});
var that = this;
rtc.onicecandidate = function(event) {
if (event.candidate) {
that.localIp = grep(event.candidate.candidate);
}
};
rtc.createOffer(function (offer) {
that.localIp = grep(offer.sdp);
rtc.setLocalDescription(offer);
}, function (error) {
console.warn('Fetching local IP failed', error);
});
var grep = function(sdpOrCandidate) {
// Does lots of string processing stuff and returns a string
}
console.log("Returning from function");
console.log(this.localIp);
}
How can I stop the function from returning until the grep function has finished doing its business and returned a value? Here's a JSFiddle demonstrating what I mean: http://jsfiddle.net/tjkxcL1j/
If you look in your browser console you should see that the getLocalIp() function is returning null first until the async stuff from rtc.onicecandidate and/or rtc.createOffer is finished.
Your function needs to accept a callback argument
Client.prototype.getLocalIp = function getLocalIp(done) {
// ...
rtc.createOffer(function (offer) {
that.localIp = grep(offer.sdp);
rtc.setLocalDescription(offer);
// call the callback here
done(null, that.localIp);
},
function (error) {
console.warn('Fetching local IP failed', error);
// call the callback with an error here
done(error);
});
};
Then you can use it like this
client.getLocalIp(function(err, ip){
if (err) return console.error(err.message);
console.log("client ip", ip);
});
However, as #zerkms mentions in a comment, this is only going to work if actually async operations are happening. Examples include accessing information over a network or accessing the disk.

Waterfall method for async.js hanging when calling mongoose save method

I'm trying to use the async waterfall method but when it gets to one of the functions, it hangs. I suspect it's because the save() operation is too slow for the execution context, but that's why I was starting to use async's waterfall, so I can wait for the value returned until it goes to the next function in the series (passing along the proper data with it which would be the counted in my case below).
// In my user controller:
async.waterfall([
function(callback) {
getSubmission(id, function(submission) {
if (submission) {
callback(null, submission);
}
});
},
function(submission, callback) {
var submissionId = submission._id;
getViews(submissionId, ip, function(count) {
if (count) {
callback(null, count, submissionId);
}
});
},
// Those top two functions work perfectly passing what
// I need to this one which is where I'm having trouble
function(views, submissionId, callback) {
// addView is called because it is actually
// inserting a row in the db, but never returns from the caller
addView(submissionId, ip, function(added) {
// this callback doesn't fire
if (added) {
callback(null, added);
}
});
},
function(added, callback) {
console.log(added);
}
]);
This is what addView() is (also within user controller which is where the previous async.waterfall code also is in) :
var addView = function(submissionId, ip, callback) {
Submission.addView({
submissionId : submissionId,
ip: ip
}, function(err, counted) {
if (err) {
throw err;
}
if (counted) {
callback(counted);
}
});
};
This is what it's calling (inside my Submission model file) when it calls Submission.addView():
exports.addView = function(obj, fn) {
var ip = obj.ip,
submissionId = obj.submissionId,
submissionView = new SubmissionView(obj);
// it gets to this point
submissionView.save({
ip : ip,
submission_id : submissionId
}, function(err, counted) {
fn(err, counted);
});
};
Whenever async "hangs", it's usually because a callback hasn't been called.
You need to make sure that you call the callback in all code paths. I would also recommend that you reserve the first parameter of any async callback to be an error, even if you don't use it as that is the pattern used throughout node.js. Some modules rely on this pattern. e.g. domains.
If you make the below change, then I would expect some error to pop up somewhere:
getSubmission(id, function(submission) {
if (submission) {
callback(null, submission);
}
});
should be something like this:
getSubmission(id, function(err, submission) {
if(err){
return callback(err);
}
if (!submission) {
return callback('no submission found');
}
callback(null, submission);
});

Categories

Resources