Significance and Reason for using a callback? - javascript

Async and callbacks are still concepts I struggle with. I have been reading and trying to make sense of code that uses them to gain more familiarity. I don't understand why this snippet of code uses a callback instead of just passing in a value (instead of an anonymous function with the argument as the value to be evaluated).
Shorten.prototype.Lookup = function (url, callback){
var mongoose = this.app.locals.settings.mongoose;
var Cursor = mongoose.model('urls');
Cursor.find({"linkFinal": url}, function(err, docs){
if (err === null){
if (typeof(callback) === "function"){
if (docs[0] !== undefined){
callback(docs[0]);
return docs[0];
}else{
callback(false);
return false;
}
}
}else{
console.log(err);
}
});
};
This is the code function in a use case:
shorten.LookUp(url, function(urlCheck){
if (urlCheck === false){
//Add to db
// another function that uses a callback as second parameter
shorten.addLink(shortenURL, shortURL){
small.shortenedURL = "http://" + domain + "/" + shortURL.link;
small.shortenError = false;
small.alreadyShortened = false;
res.json(small);
});
}else{
small.shortenedURL = "http://" + domain + "/" + urlCheck.link;
small.shortenError = false;
small.alreadyShortened = true;
res.json(small);
}
});
My only intuition for reason on why to use a callback function in this scenario is that when the query is made to the Database (in this case mongodb using Mongoose api) the function is only run when the data is available(which is the case with:
// after the result of the query is ran, the result is passed to the function in
// the docs parameter.
Cursor.find({"linkFinal": url}, function(err, docs){
This is the main reason I can come up with on why a callback is passed instead of just a value. I am still not 100% certain on why this wouldn't work:
Shorten.prototype.LLookUp = function (url, value){
var mongoose = this.app.locals.settings.mongoose;
var Cursor = mongoose.model('urls');
Cursor.find({"linkFinal": url}, function(err, docs){
if (err === null){
if (docs[0] !== undefined){
return docs[0];
}else{
value = false;
return false;
}
}
}else{
console.log(err);
}
});
};
All I did in this scenario was remove the the anonymous function and passed the argument of that function instead.
Why is a callback preferred in the first code snippet over just
passing an argument?
Would the third code snippet be a viable option? If not, why?
Thanks!

The return statements are inside the callback function, not the LLookUp function. So they will return to whoever calls the callback (mongoose library), however mongoose library doesn't do anything with the value the callback returns so it's useless to return any value.

Related

define outer scope variable inside function

i am building validation for one of form's field serverside (expressjs) and doing following actions for that:
Read data from json file
Get property from it (Array)
Check if it contains every single element of user generated array and nothing more, for example:
[1,2,3,4,5]; (json array)
[1,2,3,4,5,6] (user generated array) //must return false
[1,2,3,4,5];
[1,3,4] //must return true;
[1,2,3,4,5];
[1,2,7] //must return false;
so i am using this code for that:
const contains = (arr1, arr2) => {
arr2.every(v => arr1.indexOf(v) !== -1)
}
var match;
fs.readFile('../tags.json', 'utf8', (err, data)=>{
var JsonData = JSON.parse(data);
var tagsArray = JsonData.tags;
console.log(tagsArray)
console.log(tags)
if(tagsArray instanceof Array){
console.log('tagsArray is array')
}
if(!contains(tagsArray, tags)){
match = false
}
else{
match = true
}
console.log(match + ' blah1')
});
console.log(match + ' blah2')
if(match == false){
return res.status(409).send({
message: 'Do not provide your own tags'
});
}
but it always returns false inside fs.readFile block because it returns undefined outside fs.readFile block, so this means that contains function return undefined (i tested it)
so what is the clue for this?
Thanks!
fs.readFile is asynchronous, so any code that depends on its result (the file being read) needs to go within your callback function. (The callback function is the (err, data) => { ... } part.)
Move the console.log(match + 'blah2') and if(match == false) { ... } parts inside of the callback (after the blah1 line).
You could also look into async or use fs.readFileSync which would allow you to avoid using callback functions.
Another side point, you will want to make sure you always reach a res.send() line, i.e. when match == true in your case. Otherwise your http request will not return when match is true.
Edit:
Here's a really basic structure for express, mostly pseudocode & comments, just to illustrate callbacks:
app.post('/tags', (req, res) => {
// your setup code here
fs.readFile('../tags.json', 'utf8', (err, data) => {
console.log('readFile has finished')
// now you have heard back from readFile
// check the err and send 500 if there was a problem
// otherwise work with the file in the var data
// any other db-related stuff also goes in here, which probably
// has its own callback you need to use
db.save(data, (err) => {
// db call is done, potentially with an error
// Here you can use `res` to send http response
})
// !! again here the db is still doing its work
})
// !! anything you add here will be executed before readFile is done
console.log('readFile is probably still at work')
})
I should also point out that you want contains to return the bool value, i.e. return arr2.every(...)
You can use async/await :
async function read(){
let data = await fs.readFile(<path>);
console.log(data); //You can use data everywhere within this scope
}

How to update an array data in database using NodeJs Async?

I am new to NodeJs and I'm finding the Non Blocking and Asynchronous nature of JS extremely difficult to understand and handle,
I have a piece of code which is supposed to Iterate an array
and for every iteration, I'm supposed to make a DB update.
Can someone provide the correct implementation of Async library functions and help fix my code?
Code example -
function updateFunction(conn, requestBody, callback) {
let arr = [];
async.each(requestBody.arr, function(item, callback) {
let sqlData = []
let columns = "";
if(item.columnData != null){
sqlData.push(item.columnData);
columns += "`columnName` = ?,";
}
if(columns != ''){
columns = columns.substring(0,columns.length-1);
let sqlQuery = 'UPDATE someTable SET '+columns
+' WHERE id = "' + item.id + '"';
conn.query(sqlQuery, sqlData, function (err, result) {
if (err) {
return callback(err, false);
}
})
}
else{
return callback(null, false);
}
columns = "";
sqlData = [];
},
function(err, results) {
//Code never reaches here, don't know why
if (err) {
return callback(err, false);
}
else{
return callback(null, true);
}
});
} // END
During your database query call, on a successful query your callback is not called, therefore causing your code to never reach the final callback.
You will want to add another return statement after your if (err) { return callback(err); } to let async know your database query is finished.
And another thing, according to the docs, the async each method's final callback does not invoke with results in its callback.
A callback which is called when all iteratee functions have finished, or an error occurs. Invoked with (err).
Therefore, it is not required for you to pass a value into the callback statement within your iteratee function.
Modify your code to do this and it will work.
conn.query(sqlQuery, sqlData, function (err, result) {
if (err) {
return callback(err);
}
return callback(null);
})
Hope this helps.
conn.query(sqlQuery, sqlData, async function (err, result) {
if (err) {
return await callback(err, false);
}
})
Something like this.. so the function callback is async here and we gave await which actually waits until the return call is finished..

Curried JavaScript function for fluent API

I want to write a default callback for HTTP-requests made with superagent. The calls are all made with the async.parallel() framework and the overall result handled together. The callback should handle the results from the HTTP-requests and return a default value, if an error occured. The default value can be specified, but null will be used if it was not set.
I want to construct my handler using a fluent syntax like this:
handle(done).withDefaultValue([]) (empty array is set as default value)
handle(done) (null is used as default value)
I'm relatively new to function currying. Here's what I've tried:
I created a Node module which should be eventually used like this:
My code in handle.js
module.exports = function(done){
this.withDefaultValue = function(defaultValue){
return function(err, result){
if(err){
debug('handling error ' + err + ' and returning default value ' + defaultValue)
return done(null, defaultValue)
}
// sanity check for null and empty objects
result = _.isEmpty(result)?[]:result
done(null, result)
}
}
return this
}
My code in somefile.js
var handle = require('handle')
async.parallel([
function(done){
api.myApiCall(arg1, arg2, handle(done).withDefaultValue([]))
},
function(done){
api.myOtherApiCall(arg1, arg2, handle(done))
}
], function(err, result){
})
Above code works for the first call (the one with withDefaultValue([]), but not for the second call:
Unhandled Error: handle(...).withDefaultValue is not a function
What am I doing wrong?
This seems to do the trick:
console.log = x => document.write(x + "<br>");
function handle(func) {
var handler = function(param) {
console.log('doing stuff...');
func(param);
};
var ret = handler.bind(this, "default");
ret.withDefault = function(val) {
return handler.bind(this, val);
}
return ret;
}
function done(param) {
console.log(param)
}
setTimeout(handle(done));
setTimeout(handle(done).withDefault(123));

Using callback function to change the value of a variable defined outsite of getJSON

Ok, so I have this function.
function check(username){
var result = "default";
$.getJSON('https://api.twitch.tv/kraken/streams/' + username, function(data){
if(data.stream == null)
result = 'offline';
else
result = 'online';
}).fail(function(){
result = 'notfound';
});
return result;
}
console.log(check('freecodecamp'));
The problem is that what I received in console log is "default", not "offline", nor "online, nor "notfound" as I expect.
I tried to move the console.log() line before the check() function but it doesn't work. I also tried to define the var result globally but it doesn't work either.
Any help would be appreciated !
This is how your code should be written:
function check(username, callback){
var result = "default";
$.getJSON('https://api.twitch.tv/kraken/streams/' + username, function(data){
if(data.stream == null) {
result = 'offline';
} else {
result = 'online';
}
callback(result);
}).fail(function(){
result = 'notfound';
callback(result);
});
}
check('freecodecamp', function (result) {
console.log(result);
});
This is because $.getJSON is an asynchronous function, so it returns immediately, while providing its output value through a callback function.
So to get your "return" value, you need to do the same thing i.e. provide a callback to your own function which is invoked when the $.getJSON invokes its own callback.

Node.js callback functionality

I'm new to the Node.js platform and I'm trying to learn as much as I can. After playing with callbacks one thing really confuses me:
So, I have this function :
function registerUser(body, res, UserModel){
var userJSON = {
email : body.email,
password : body.password,
accessToken : null
};
var user = null;
var userAlreadyExists = false;
UserModel.find({}).select('email').exec(function(err, results){
if(err){
console.log('Database error : ' + err);
// send the appropriate response
}else{
for(var index in results){
if(results[index].email == userJSON.email){
userAlreadyExists = true;
break;
}
}
if(userAlreadyExists){
// send the appropriate response
}else{
newAccessToken(UserModel, function(error, token){
if(error != null){
// handle the error
}else{
userJSON.accessToken = token;
user = new UserModel(userJSON);
user.save(function(err){
if(err){
// .. handle the error
}else{
// .. handle the registration
}
});}});}}});}
And then the function which accepts the callback:
function newAccessToken(UserModel, callback){
UserModel.find({}).select('email accessToken').exec(function(err, results){
if(err){
callback(err, null);
}else{
// .... bunch of logic for generating the token
callback(null, token);
}
});
}
I would expect the callback to not work(maybe throw an error) since both user and userJSON are not defined in it's context.(well, that's not exactly true, but since it is executed async - after a while - , I would expect the callback to lose it's references to those variables, which were defined locally in the registerUser function). Instead this example works perfectly, the callback function keeps it's references with those two variables defined in the registerUser function. Could somebody explain me how the async callback and the references work and why does the example work?
Instead of callbacks, those are called closures, and in JavaScript the scope treatment is special. Check this document:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
H i, the function you 'calllback to' is within the scope of the variables you are trying to access, so all good to go for accessing them.
This is not a nodejs thing, regular JS works the same way .
The difference
1) Will not be able to access a var called 'foo'
function finishfunction() {
console.log(foo); /* undefined */
}
function functionwithcallback(callback) {
callback();
}
function doStuff() {
var foo = "bar";
functionwithcallback(finishfunction);
}
doStuff();
2) like yours, access to 'foo' is fine.
function functionwithcallback(callback) {
callback();
}
function doStuff() {
var foo = "bar";
functionwithcallback(function() {
console.log(foo) /* all fine */
});
}
doStuff();

Categories

Resources