How to handle callbacks? - javascript

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 !!

Related

Bug in custom fs.readfile callback function to return on SUCCESS or ERROR

I am trying to implement a decorator on the fs.readFile function. Instead of the regular error and data params, this version should take two callbacks as arguments (both after the file name) – one to be called on success and one to be called on failure. Both callbacks only have one parameter (the data read from the file or an error object). The actual implementation simply calls fs.readFile.
I can't figure out why this isn't working, and what I'm doing wrong. Please help me debug this. Thank you.
function myReadFile(fileName, successFn, errorFn) {
fs.readFile(fileName,'utf8', function read(errorFn, successFn) {
if (errorFn) {
errorFn();
}
else {
successFn();
}
});
}
fs.readFile callback does not return an errorFn or successFn.
Example from Node.js fs docs:
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err;
console.log(data);
});
Instead you can pass the err object into your errorFn and same for success with data.
function myReadFile(fileName, successFn, errorFn) {
fs.readFile(fileName,'utf8', function read(err, data) {
if (err) return errorFn(err);
return successFn(data);
});
}
Alternatively you could turn it into a Promise like so:
function myReadFile(fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName,'utf8', function read(err, data) {
if (err) return reject(err);
return resolve(data);
});
});
}
//usage:
myReadFile('/some/file')
.then(data => {
//data here
})
.catch(err => {
//error here
});

Callbacks and Async functions

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

Javascript Promise prematurely resolving

I have a function that returns a Promise, that accesses the database and pulls a few lines out, assigning them to a Javascript variable.
The issue is that my '.then' clause is being triggered even though I know the Promise hasn't resolved:
app.post("/api/hashtag", function (req, res) {
FindPopularRumours().then(function (resolveVar) {
console.log(resolveVar);
console.log();
res.send(resolveVar);
}).catch(function () {
console.log("DB Error!");
res.send("DB Error!");
});
});
And the Promise function:
function FindPopularRumours() {
return new Promise((resolve, reject) => {
var hashtags = [];
var dbPromise;
db.collection(HASHTAGS).find().forEach(function (doc) {
hashtags.push(doc.hashtag);
console.log(hashtags);
});
resolve(hashtags);
});
}
The result output is:
[ ]
['#test1']
['#test1', '#test2']
['#test1', '#test2', '#test3']
As you can see, the first line ('[ ]') should ONLY be executed AFTER the hashtags have been output. But for some reason my code seems to think the Promise has been resolved before it actually has.
EDIT1
As per Ankit's suggestion, I have amended my function to:
function FindPopularRumours() {
return new Promise((resolve, reject) => {
var hashtags = [];
db.collection(HASHTAGS).find({}, function (err, doc) {
if (!err) {
doc.forEach(function (arg) {
hashtags.push(arg.hashtag);
console.log(hashtags);
});
resolve(hashtags);
} else {
return reject(err);
}
});
});
}
This still returns the same output response as before (e.g the 'then' clause is running before the promise itself).
My POST function is still the same as before.
The db.collection.find() function is async, so you have to resolve the promise inside the callback for that, something like
function FindPopularRumours() {
return db.collection(HASHTAGS).find().toArray().then( (items) => {
return items.map( doc => doc.hashtag);
});
}
takes advantage of the Mongo toArray() method, that returns a promise directly
Please note that db.collection(HASHTAGS).find() is an asynchronous call. So, your promise is resolved before database query returns. To solve this problem, you need to re-write your database query as follows:
function FindPopularRumours() {
return new Promise((resolve, reject) => {
var hashtags = [];
var dbPromise;
db.collection(HASHTAGS).find({}, function(err, doc){
if(!err){
doc.forEach(function (arg) {
hashtags.push(arg.hashtag);
console.log(hashtags);
});
resolve(hashtags);
}else{
return reject(err);
}
});
});
}
Hope the answer helps you!

Conditional async function

I have my function getting an email from Gmail. I want to run this function n times or until an email is found.
What is a proper way to do it? I tried: http://caolan.github.io/async/docs.html#retry but without success.
I was following this article how to read emails: https://developers.google.com/gmail/api/quickstart/nodejs
Assuming you have a routine called gmail, which returns a promise which succeeds (fulfills) if an email is found, and otherwise fails (rejects), then:
function get(n) {
return gmail().catch(() => {
if (!n) throw "Too many tries!";
return get(--n);
};
}
Usage:
get(5).then(
mail => console.log(mail.body),
() => console.log("No mail!"));
If for some reason you do not like the recursive style:
function get(n) {
let promise = Promise.reject();
do { promise = promise.catch(gmail); } while (n--);
return promise;
}
If gmail is callback style, then
function get(n, cb) {
gmail(function(err, data) {
if (err)
if (!n) get(--n, cb);
else cb("Too many tries!");
else cb(null, data);
});
}
Or better yet, promisify gmail, either using a library or
function promisify(fn) {
return new Promise((resolve, reject) {
fn(function(data, err) {
if (err) reject(err);
else resolve(data);
});
});
}
and then replace gmail in the first solution with promisify(gmail).

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.

Categories

Resources