So I'm working on a movie website to practice my node.js/angular skills. Recently I noticed that I have a lot of nested asynchronous functions on my server side. Many people suggested using the async.js module for this.
Below there are two different versions of the same server side objective. One is the original nested async functions and the other one is what I came up with using Async.js. Could someone see if I did it the right way, used the right method in this case (waterfall) and perhaps give me some corrections that could improve my code? It works, but maybe you guys can see some improvements? For example: Is there an alternative to passing the variables to the arguments every time ( like I did with reviewId e.g)
Extra question:
What are the most used methods of the async.js module? Cause there are like hundreds and I would just like to learn the most important ones.
This is what I came up with when I tried to translate the nested asynchronous functions below
router.delete('/deleteReview/:reviewId', function(req, res, next) {
async.waterfall([
function (callback) {
var reviewId = req.params.reviewId;
Review.find({'_id': reviewId})
.populate('user')
.populate('movie')
.exec(function(err, review){
callback(null, review, reviewId)
});
},
function (review, reviewId, callback) {
var index = review[0].user.reviews.indexOf(reviewId);
review[0].user.reviews.splice(index, 1);
review[0].user.save(function(err, user){
callback(null, user, reviewId, review)
})
},
function (user, reviewId, review, callback) {
var index = review[0].movie.reviews.indexOf(reviewId);
review[0].movie.reviews.splice(index, 1);
review[0].movie.save(function(err, movie){
callback(null, movie, index)
});
}
], function (err, result, index) {
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
res.status(200).json({
message: "successful",
obj: index
})
});
});
the original nested asynchronous functions
router.delete('/deleteReview/:reviewId', function(req, res, next){
var reviewId = req.params.reviewId;
Review.find({'_id': reviewId})
.populate('user')
.populate('movie')
.exec(function(err, review){
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
var index = review[0].user.reviews.indexOf(reviewId);
review[0].user.reviews.splice(index, 1);
review[0].user.save(function(err, user){
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
var index = review[0].movie.reviews.indexOf(reviewId);
review[0].movie.reviews.splice(index, 1);
review[0].movie.save(function(err, movie){
if (err) {
res.status(500).json({
message: "An error occurred",
obj: err
})
}
res.status(200).json({
message: "successful",
obj: index
})
})
});
})
});
Related
In the below code, users.push used within ‘db.each’ wont work. However, if I move ‘users.push’ outside then it seems to work.
How can I push the new objects from db.each into the users array?
let db = new sqlite3.Database('./db/main.db', (err) => {
if (err) console.error(err.message);
console.log('Connected to the main database.');
});
var users = [];
db.serialize(() => {
db.each(`SELECT email, name FROM users`, (err, row) => {
if (err) console.error(err.message);
let user = {
email: row.email,
name: row.name
}
users.push(user);
});
});
console.log(JSON.stringify(users));
db.close();
I am using express and sqlite3 node packages.
It's because db.serializeand db.each are asynchronous functions (and return immediately, thus executing console.log before the db callbacks are executed).
Here should be a working example :
db.serialize(() => {
db.each(`SELECT email,
name
FROM users`, (err, row) => {
if (err) {
console.error(err.message);
}
let user = {
email : row.email,
name : row.name
}
users.push(user);
console.log(JSON.stringify(users));
db.close();
});
});
First error: asynchronicity not handled properly
As Antoine Chalifour pointed out, you call console.log(JSON.stringify(users)); before users gets modified in the asynchronous callback. Refer to his answer for fix and explanations.
Second error: errors not handled
You wrote if (err) { console.error(err.message); } then go on with the rest of the function. That is bad, because an error might happen and you'd just continue with your program. You should instead write something like:
if (err) {
console.error(err);
return;
}
or:
if (err) throw err;
I'm currently working with mongoose and I've come across something which I find very strange.
My code is as follows:
Challenge.findOneAndUpdate({ _id: req.params.challengeId, users: req.user._id }, { $push: { submissions: submission._id } }, { new: true })
.then(function(err, challengeUpdated) {
console.log(challengeUpdated);
console.log(err);
if (err) {
console.log('The error is : ');
console.log(err);
return res.status(401).json({ message: 'Error saving submission' });
}
else {
return res.json(submission);
}
})
Note how I have function(err, challengeUpdated), in the docs here: http://mongoosejs.com/docs/api.html#model_Model.findOneAndUpdate, it says it should be this way round, but when I log them I err prints the object, and challengeUpdated prints either undefined / null. Changing the parameters around seems like I hack, so I was wondering whether there was something obvious that I'm doing wrong here.
Note: It is possible for the req.user._id not to be in users. Which is the error that I'm trying to find.
Looking at the answers to this question: Mongoose: findOneAndUpdate doesn't return updated document, it seems like the answers have it the same way round as me.
Hoping someone else faced this issue?
Note: I'm using mongoose v5.5.1.
Your problem is that you are using error-first callback arguments in Promise-based code.
This is how it should be when using Promise interface:
Challenge.findOneAndUpdate({ _id: req.params.challengeId, users: req.user._id }, { $push: { submissions: submission._id } }, { new: true })
.then((challengeUpdated) => {
console.log('Success!')
console.log(challengeUpdated)
})
.catch((err) => {
console.error('An error occured', err)
})
function(err, result) { } is a pattern reserved for callback-style code, not for Promises.
I have an API in Node.js. My routes look like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(next);
};
As seen in the example, I use .catch(next). But is this the correct way to do it? Shouldn't the route always print json?
So I am thinking of doing something like
exports.getUserList = (req, res, next) => {
User.find().sort({ name: 1 }).then(users => {
res.json({
status: 'success',
users
});
}).catch(err => {
res.json({
status: 'error',
msg: err
});
});
};
but shouldn't it then be something like res.status(some_status_code).json({})?
How is a simple API normally carried out in terms of error handling?
What if I, in the code, use a variable that is not defined (i.e. causing a syntax error)? Should I handle it with a JSON error or should I just make sure that I don't do sloppy coding? :-D
Also, is this the fastest way to print the json? I mean, should I use User.find().lean()? Should I do some caching? Is it even clever to store my API on a normal website or are there optimized API servers for such cases?
Have you try async/await function and custom response for success and error?
Here the example :
responseHandler.js
// use this for success
function successResponse(message, data) {
const success = {
success: true,
message: message,
data: data
}
return success
}
// use this as middleware error handler
function errorResponse(err,req,res,next) => {
return res.status(err.status || 500).json({
success: false,
message: err.message
})
}
module.exports = {
successResponse,
errorResponse
}
myRouter.js
const { successResponse } = require('./responseHandler')
exports.getUserList = async (req, res, next) => {
await User.find().sort({ name: 1 }).then(users => {
res.status(200).json(
successResponse(`data user`, users)
)
}).catch(err => {
return err
});
};
i want to simply handle error and get insertId after created new row on database, in my code which i get help from mysqljs/mysql official page, i can't do them, how can i fix my code to do them?
var post = {deviceImei: deviceInformation.deviceImei, created_at: currentDateTime, updated_at: currentDateTime};
var query = connection.query('INSERT INTO users SET ?', post, function (err, result) {
if (err)
throw err;
return result;
});
query.on('error', function (err) {
console.log(err)
}).on('fields', function (fields) {
console.log(fields)
}).on('result', function (row) {
console.log(row.insertId);
}).on('end', function () {
// all rows have been received
});
after insert or get some error i cant any message on shell by console
The correct way to do that is documented here:
connection.query('INSERT INTO users SET ?', post, function(err, result) {
if (err) throw err;
console.log(result.insertId);
});
You can use native ES6 promises supported in newest versions of node
var insertQuery = function(post) {
return new Promise(function(resolve, reject) {
connection.query('INSERT INTO users SET ?', post, function (err, result) {
if (err)
reject(error);
resolve(result);
}
});
}
The you can handle after
insertQuery(post)
.then(function(result) {
console.log(result.inserId);
})
.catch(function(error) {
console.log(err);
})
There are libs like 'q' or 'bluebird' which are easy to use too
I am attempting to use mongoose with async, everything works fine for the most part...however when I perform a lookup that returns no results my application seems to hang and eventually timeout.
Here is some example controller code that does a simple lookup by id using mongoose and async:
module.exports.find = function(req, res) {
async.waterfall([
function(next) {
SomeModel.findById(req.params.id, next);
},
function(someModel, next) {
if (!SomeModel) {
res.status(404).json({
success: false,
message: 'SomeModel not found'
});
} else {
res.json(SomeModel);
}
}
]);
};
If a record is found everything comes back just fine, however for a nonexistent id it seems that the second async step is never called and eventually the whole request times out.
So what am I doing wrong here? How do I get the 'findById' method to call 'next' even if a record isn't found?
Mongoose is throwing an error, and you aren't catching it. The other thing I should mention is that you should be doing your response handling in the final callback (which you have not defined).
Try something like this:
module.exports.find = function(req, res) {
async.waterfall([
function(next) {
SomeModel.findById(req.params.id, next);
}
], function(err, SomeModel){
// this is the final callback
if (err) {
// put error handling here
console.log(err)
}
if (!SomeModel) {
res.status(404).json({
success: false,
message: 'SomeModel not found'
});
} else {
res.json(SomeModel);
}
});
};
Alternatively, you could just simplify it to not use waterfall:
module.exports.find = function(req, res) {
SomeModel.findById(req.params.id, function(err, SomeModel){
if (err) {
// put error handling here
console.log(err)
}
if (!SomeModel) {
res.status(404).json({
success: false,
message: 'SomeModel not found'
});
} else {
res.json(SomeModel);
}
});
};