I am learning about building a new user login app and there is a line of code the makes me quite confused and Google did not show me any enlightening result.
module.exports.createUser = function(newUser, callback){
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
It is quite straightforward: The new user module is trying to crypt the password and runs the callback(It is my understanding). But this line here:
newUser.save(callback);
It is very confusing to me and would anyone explain what it does here?
Thank you
You pass a callback function as a parameter to the outer most function. And when the genSalt- and hash-functions are done, you pass the same callback function as a parameter to newUser.save(). It is a function that (probably) will be executed inside newUser.save().
Sometimes using callbacks are a way to say that some code has completed, and then run a new function. Useful for async funcions.
A simpler example:
serveCake = function(){ // this is my callback function
// serve the cake
}
createCake = function(ingredients, callback){ // pass serveCake as a callback here.
// Mix the ingredients and put the cake in the oven
// When the cake is finished baking, serve it
callback(); // call the callback function.
}
const ingredients = ['suger', 'butter'];
createCake(ingredients,serveCake);
The same happens with your code, but the callback function will (probably) be executed inside the newUser.save()-method.
Mongoose save method expects an optional callback function as an argument (Reference Doc). So when you are calling newUser.save(callback); you are passing the same callback function that you got in the createUser(newUser, callback); so whichever method will call this method i.e. suppose in following code callIt() function is calling your code.
module.exports.createUser = (newUser, callback) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
newUser.password = hash;
newUser.save(callback);
});
});
};
function callIt() {
module.exports.createUser({ name: 'ridham' }, function (err, user) {
if (err) {
console.log(error);
} else {
console.log(user);
}
});
}
callIt();
so here you are giving function (err, user) {...} function as an argument which can be called by invoked function to pass back the access to invoking the function(handling async nature of JS). and your invoke function again invoking another function passing the same callback reference so basically, you are chaining the same callback.
Callback Explanations can be found here and here.
Related
I am trying to get data from a MongoDB collection so I created a database.js module to do this. One of the functions on the module was an exports.find function used to find data from a collection. I pass in a callback into the function because the Javascript is asynchronous and it takes time to retrieve data from the server. This is the code of the exports.find.
exports.find = (database, table, projection, callback) => {
MongoClient.connect(url, (err, db) => {
if (err) throw err
var dbo = db.db(database);
dbo.collection(table).find({}, projection).toArray(function(err, results) {
if (err) throw err
callback()
db.close()
});
});
}
I then use this function in my index.js file to use the found data and send it to the client. I did this by passing in res.render into my callback.
app.get('/', (req, res) => {
database.find('blog', 'blog-posts', {}, res.render('pages/index'))
})
Whenever I run this however, I get this error
TypeError: callback is not a function
at C:\Users\21Swag\Documents\Coding\Blog\database.js:23:13
The error points to the database.js file when I call the callback.
callback()
Does anyone know how to fix this? Any help would be appreciated.
Your callback is not a function, because you are already invoking it in database.find and then trying to call the return value of said function as your callback.
Maybe this will help:
const one = () => console.log(1)
const cbTest = (cb) => cb()
cbTest(one()) // Error: cb is not a function as it already returned 1
cbTest(one) // will console.log 1 as it is invoked correctly
Usually a callback is passed as an anonymous function like this:
database.find('blog', 'blog-posts', {}, () => res.render('pages/index'))
You can see the same pattern in the MongoClient.connect function/method. The second argument is the anonymous callback with (err, db) as parameters.
I understand callback functions conceptually, but I don't understand how they are understand in Node.js and am quite confused by the syntax. Can someone give me a simple explanation for each line of code which is running? The code works, but I don't understand why.
var removeById = function(personId, done) {
Person.findByIdAndRemove(personId, function(err, data) {
if(err) {
done(err);
}
done(null, data);
});
};
Line by line explanation.
Line 1 (assume)
var removeById = function(personId, done) {
done is the formal identifier for callback that you'll pass later when you call removeById function
Line 2
Person.findByIdAndRemove(personId, function(err, data) {
findByIdAndRemove expects 2nd parameter to be a function with two parameters, first err, this will hold error and 2nd data, this will hold data/result
Line 4
done(err)
Line 6
Call your callback with error
done(null, data)
call your callback with first parameter as null (probably intended to signal there is no error) and data which would hold the data/result
Extra note:
The callback you pass to removeById should also (preferably, if you're not doing anything else with it) expect 2 parameters, same as callback passed to findByIdAndRemove
The Person.findByIdAndRemove is basically something like :
Person.findByIdAndRemove = function(personId, callback) {
// The code is executed
// call function on error | success
callback(err, data);
};
The callback you want to execute should be something like:
const done = function(err, data) {
if(err) {
console.log(err);
}
console.log(data);
}
Your code :
var removeById = function(personId, done) {
Person.findByIdAndRemove(personId, function(err, data) {
if(err) {
done(err);
}
done(null, data);
});
};
Usage:
removeById(3, done);
Probably this will be an easy question because I don't understand the concept of callback (I thought I did, but apparently no).
I'm trying to return a response from MySQL (easy thing to do in almost every language but with nodeJS):
I'm calling a function from my routes file:
route
const user = require('../models/Users');
router.post('/register', (req, res, next) => {
let newUser = {
username: req.body.username,
password: req.body.password
};
if(user.checkIfUserExists) {
// do something
}
});
And here's the query inside another file:
const mysql = require('../handlers/mysql');
const bcrypt = require('bcrypt-nodejs');
var User = {
checkIfUserExists: function(newUser, callback) {
mysql.query('SELECT * FROM users where Email like ?', [newUser.username], function(err, result) {
if(err) return callback(err);
callback(null, result > 0);
});
},
registerUser: function(newUser) {
mysql.query("INSERT INTO users (Email, Password) VALUES (?, ?)", [newUser.username, User.hashPassword(newUser.password)], function(err, results) {
if (err) throw err;
return true;
});
},
hashPassword: function(password) {
let salt = bcrypt.genSaltSync(10);
return bcrypt.hashSync(password, salt);
}
}
module.exports = User;
Of course, same thing will happen with registerUser
You forget to call and handle the callback.
user.checkIfUserExists(newUser, function(arg1, isUserExist) {
if (isUserExist) {
//user exist
}
});
You are not using callback. To do that follow the below code.
`user.checkIfUserExists(newUser,(result)=>{
If(result){
//write your code
}
}`
Many guys have explained you problem. Let me explain you the concept of callback.
Callback is the function that is called, after a function has finished their job
For example, this function
setTimeout(function(){
alert('Alert will be ran after 3 seconds');
},3000);
You can open the developer mode (hit f12 on your browser and run it). You can see that the alert box will display after 3 seconds. I can explain the flow of code like this.
setTimeout function is run. This function will be finished after 3000 milliseconds, after that it will run the callback function
After 3000 milliseconds, the callback function is called. So the alert box is displayed.
In your code, you just create a plain new object, it does not have the checkIfUserExists function which is from the User object. You have to create user from the User class.
The callback function caused a lot of trouble to beginner, that's why in ES6+ (Javascript Version 6+). The class, async, await are introduced and make life easier.
I am trying to call two functions and pass the output of the first function as a parameter into the second.
Function 1:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err)
throw err;
if(user)
callback(null, user.statistics);
});
}
Function 2:
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err)
throw err;
if(statistics)
callback(null, statistics.game);
});
};
I am trying to execute the second method by passing the output of the first method as a parameter but the asynchronous nature of javascript is messing it up. I have tried implementing promises to no avail.
Can anyone suggest some good javascript practices to deal with calling functions asynchronously when they need each other? Any help would be appreciated.
After fixing the issue I mentioned above, you can call them in sequence like this:
module.exports.getAllStatisticsByUserId = function(id, callback){
User.findById(id, (err, user) =>{
if(err) callback(err);
if(user) callback(null, user.statistics);
});
};
module.exports.getGameByStatisticsId = function(id, callback){
Statistics.findById(id, (err, statistics) =>{
if(err) callback(err);
if(statistics) callback(null, statistics.game);
});
};
someService.getAllStatisticsByUserId(id, (err, statistics) => {
if (err || !statistics) {
// handle error
return;
}
someService.getGameByStatisticsId(statistics.id, (err, game) => {
if (err || !game) {
// handle error
return;
}
// handle game
});
});
However, as noted in Mongoose documentation:
When a callback function is not passed, an instance of Query is returned, which provides a special query builder interface.
A Query has a .then() function, and thus can be used as a promise.
So you can simply rewrite the calls like this:
someService.getAllStatisticsByUserId(id).then(statistics =>
someService.getGameByStatisticsId(statistics.id)
).then(game => {
// handle game
}).catch(err => {
// handle error
});
or convert it into an async/await function:
async function getGameByUserId(id) {
try {
const statistics = await someService.getAllStatisticsByUserId(id);
const game = await someService.getGameByStatisticsId(statistics.id);
// handle game
} catch (error) {
// handle error
}
}
Note that an async function always returns a Promise, so you must await it or chain it with a .then() to ensure completion of the query and resolve the returned value, if any.
It looks like you should be able to write:
getAllStatisticsByUserId("me", (err, stats) => {
getGameByStatisticsId(stats.id, (err, game) => {
console.log(game);
});
});
Here's how it would look if these functions returned promises instead.
getAllStatisticsByUserId("me")
.then(stats => getGameByStatisticsId(stats.id))
.then(game => console.log(game))
Even better, if you're able to use a version of Node that supports async/await then you could write.
let stats = await getAllStatisticsByUserId("me");
let game = await getGameByStatisticsId(stats.id);
console.log(game);
This would mean slightly rewriting the original functions (unless User.findById and Statistics.findById already return promises).
module.exports.getAllStatisticsByUserId = function(id, callback){
return new Promise((resolve, reject) => {
User.findById(id, (err, user) =>{
if(err) return reject(err);
return resolve(user.statistics);
});
});
}
This question already has answers here:
Understanding function (err, data) callbacks
(4 answers)
Closed 5 years ago.
I have been following a Node.js tutorial. I always had a doubt in my mind how data are passed to callback function parameters. As an example
User.addUser(newUser, (err, user) =>{
if(err){
res.json({success: false, msg:'Failed to register new user'});
} else {
res.json({success: true, msg:'User registered'});
}
});
and addUser function is defined as,
module.exports.addUser = function(newUser, callback){
bcrypt.genSalt(10, (err,salt)=>{
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser.save(callback);
});
});
}
I don't understand how err and user are passed. Can someone explain this?
The answer is that newUser.save also takes a callback parameter and passes it the same kind of parameters. So you can just pass the callback straight into save. I imagine user.save would look something like the following:
User.prototype.save = function(callback) {
//do stuff to save the user
//maybe get an error in the process, or a user record, pass them to the callback
callback(saveError, userRecord)
}
Because the expected args for save and addUser are the same, the callback can be passed straight into save.
Edit:
I would however suggest to invoke your callback with the error if one is returned from the bcrypt call. Since you already have a callback to give the error to, it doesn't make much sense to throw. Callers will expect errors to come in the callback, so I suggest this instead:
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) {
callback(err, null)
return
}
...