I have been looking to update my Express skills by incorporating async/await handling and had a quick question.
From the examples I have seen online, most requests are structured wrapped inside of a try/catch block and add any await tasks to a variable before handling them.
app.post('/signup', async(req, res) => {
const { email, firstName } = req.body
const user = new User({ email, firstName })
const ret = await user.save()
res.json(ret)
})
My code looks like this:
app.route("/articles")
// GET: articles
.get(async (req, res) => {
await Article.find((err, results) => {
if (!err) {
res.json(results);
} else {
res.send(err);
};
});
})
Should I assign the response from my Mongoose to find to a variable as the first code block example and handle in a try/catch, or does my code essentially do the same thing and in a way that's the best practice as is?
Thanks in advance!
Cheers,
James
Be aware that there are two very different uses of the word async in javascript. The first is the general concept of asynchronous functions which can be implemented in any language and is widely used in javascript with various design patterns (callback, promises, async/await). The second is the async keyword which is used to allow the usage of await and only works on promises (does not work in callbacks).
You seem to be confusing the two. Because of this I now advise people not to use the word "async" when referring to asynchronous functions and only use it to refer to the async keyword.
Your function does not return a promise because you passed a callback to it. As such it cannot be used with await. Because you cannot use await it makes no sense to mark the function as async. I consider your code buggy even though it works - the mechanism still function (the await keyword conveniently ignores non-Promise functions like yours and does not generate an error) however it doesn't communicate your intent well and will confuse future maintainers of your code.
IMHO, the correct version of your code should be:
app.route("/articles")
// GET: articles
.get((req, res) => {
Article.find((err, results) => {
if (!err) {
res.json(results);
} else {
res.send(err);
};
});
})
The correct version of your code with async/await is:
app.route("/articles")
// GET: articles
.get(async (req, res) => {
try {
res.json(await Article.find());
}
catch (err) {
res.send(err);
}
})
The correct version of your code with promises but without async/await should be:
app.route("/articles")
// GET: articles
.get((req, res) => {
Article.find()
.then(result => res.json(result))
.catch(err => res.send(err));
})
The above is of course just my opinion but I suggest you strongly consider it a guideline. Any of the three forms above would be perfectly acceptable to most javascript programmers and most people consider which to use a matter of taste. Personally I prefer plain promises without await but async/await is useful when you have some tricky flow control logic.
Note that Mongoose conveniently supports both promises and callbacks so in this specific case you can just remove the callback to use async/await. However not all libraries do this. If you need to convert a callback based function to a promise you need to wrap it in the Promise constructor:
function convertedToPromise () {
return new Promise((resolve,reject) => {
callbackBasedFunction((err,result) => {
if (err) {
reject(err)
}
else {
resolve(result)
}
});
});
}
Related
I'm not experienced with Javascript promises and recently I started using promises instead of callbacks in my Javascript projects.
When I tried to run several promise functions one after another I landed in a nested chaos of then(). The code works exactly as expected, but my question is that if this is the way to resolve several promise functions one after another then what is the advantage of using promises instead of callbacks.
If I'm not doing it the right way, then it is a request from you guys to show me the proper way of resolving nested promises.
Below is my code that I don't like it they way it looks:
exports.editExpense = (req, res, next) => {
Account.findAll().then(accounts => {
Budget.findAll().then(budgets => {
Expense.findAll().then(expenses => {
Expense.findByPk(id).then(expense => {
res.render('expenses/index', {
urlQuery: urlQuery,
expenses: expenses,
expense: expense,
accounts: accounts,
budgets: budgets
});
})
})
})
}).catch(error => console.log(error));
};
You can use async/await structure for better formatting
exports.editExpense = async(req, res, next) => {
try {
let accounts = await Account.findAll();
let budgets = await Budget.findAll();
let expenses = await Expense.findAll()
let expense = await Expense.findByPk(id);
if (expense) {
res.render('expenses/index', {
urlQuery: urlQuery,
expenses: expenses,
expense: expense,
accounts: accounts,
budgets: budgets
});
} else {
console.log('else') //<<- Render/Handle else condition otherwise server will hang.
}
} catch (error) {
console.error(error)
}
You should try to minimize the amount of async calls you make in a function as it will impact your performance.
If you prefer to use the then catch structure, in order to take fully advantage of it I recommend you not to nest them. Of course you can, but then you should put a .catch() after each of them. That's why the async introduction made an easier code to read and handle errors, as it simplifies it with the try catch structure.
If you pipe multiple .then(), you can return a value as a promise from each of them that can be used inside the next one once the promise resolves. The only thing is that you loose these values unless you save them either in req with new properties or in variables declared outside the pipe of .then().
That's why, in this snippet, I declared all the variables at the beginning in order to save all the values and use them in the final res
exports.editExpense = (req, res, next) => {
let accounts;
let budgets;
let expenses;
Account.findAll()
.then(fetchedAccounts => {
accounts = fetchedAccounts;
return Budget.findAll()
})
.then(fetchedBudgets => {
budgets = fetchedBudgets;
return Expense.findAll()
})
.then(fetchedExpenses => {
expenses = fetchedExpenses
return Expense.findByPk(id)
})
.then(expense => {
return res.render('expenses/index', {
urlQuery: urlQuery,
expenses: expenses,
expense: expense,
accounts: accounts,
budgets: budgets
});
})
.catch(error => console.log(error));
};
router.get('/:id',ensureLogin,(req,res)=>{
somemodel.findOne({...}) ****//mongoose call 1****
.then(result=>{
if(some condition){
User.findOne(some condition) ***//mongoose call 2***
.then(result=>{
//Some code is to be executed here using the "User.findOne..."
}
res.render('some template')
.catch(err=>{
console.log(err)
})
}
else{
res.render('some another template');
}
})
.catch(err=>{
res.render('error/500');
})
})
Here mongoose call 2 depends on mongoose call 1 for some reason. Though the code is working yet I want it to be framed properly. So please suggest a better way for doing this. I hope I cleared enough the query.
Thanks.
PS: I'm a newbie in NODE.js
The asynchronous nature of a lot of Node.js calls can be very confusing at first, but you get used to it quite quickly and realize that it's a great technology.
I'd suggest using the async / await syntax, this makes the code much more like synchronous code. It certainly makes it a lot easier to follow.
I'd refactor the code to something like this:
router.get('/:id', ensureLogin, async (req,res) => {
try {
let result = await somemodel.findOne({/* search criteria */});
if (someCondition(result)) {
let user = await User.findOne({/* search criteria */})
res.render('some template');
} else {
res.render('some another template');
}
} catch (error) {
res.render('error/500');
}
})
You would benefit from using async/await, particularly with conditionals involving different asynchronous execution branches which you have. It also makes it a bit easier to have centralized error handling for the different asynchronous code paths:
router.get('/:id', ensureLogin, async (req,res) => {
try {
let result = await somemodel.findOne({...});
if(some condition){
let secondResult = await User.findOne(some condition);
//Some code is to be executed here using the "User.findOne..."
res.render('some template')
} else {
res.render('some another template');
}
} catch(e) {
res.render('error/500');
}
}
Could be a bad practice to have all functions on a nodejs Express web app with async await syntax? (even if they not necessarily uses await methods) for example:
app.get(basePath, async function (req, res, next) {
await anyMethod().then(function () {
})
await anyMethod2().then(function () {
})
});
app.get(basePath, async function (req, res, next) {
anyMethod().then(function () {
})
});
The idea is to have allways defined like async for terms of templating or if I need to use them like this other example for best practices:
app.get(basePath, async function (req, res, next) {
await anyMethod().then(function () {
})
await anyMethod2().then(function () {
})
});
app.get(basePath, function (req, res, next) {
anyMethod().then(function () {
})
});
could be affect performance?
(I removed params and the logic of the promises for easy visualization of the question)
could be affect performance?
async/await impact always on performance.
If you add async in a synchronous function, you have a degradation of performance of 400%. A simple banchmark
Adding await in Node.js <= 11 create 2 Promises at the low level, one then wrap the upper code, and another one for the next literals.
With Node.js 12 await will produce only one additional Promise.
v8 and Node.js are working hard to reduce this impact on performance. V8 Article that explains how async/await works under the hood.
Looking your example, instead, it is not good because you are waiting for nothing:
await anyMethod2().then(function () { res.reply('hello') })
If you don't use the result of an await it is pointless because the output will be the same in the end:
await anyMethod2().then(function () { res.reply('hello') })
anyMethod2().then(function () { res.reply('hello') })
These two sentences produce the same result, but with different overhead (and error management).
And you have to think if anyMethod1() and anyMethod2() could be parallel or serial. In your example, you have lost the parallelism.
This is worth:
const res = await anyMethod2().then(function () { return {hi:'world'} })
res.reply(res)
If you await, use the output!
I have a route in express with a function that adds some req.body attributes info to a mongo collection. It works, but I’m not happy with it, it feels wrong to me, and I don’t think I’ve got my head around async/await yet.
This is the code:
router.post("/input/:id", ensureAuthenticated, async (req, res) => {
Project.findOne({
_id: req.params.id
}).then(project => {
const newInput = {
inputTitle: req.body.inputTitle,
inputValue: req.body.inputValue,
inputUnits: req.body.inputUnits,
inputCategory: req.body.inputCategory,
inputUser: req.user.id
};
// Add to the array, this is the async function
(async () => {
await project.inputs.unshift(newInput);
})();
project.save().then(project => {
res.redirect(`/projects/output/${project.id}`);
});
});
});
Is that async function doing what I think/hope it’s doing (I want it to add the newInput variable to the database before the page redirects)? What's the best way of doing this? Should I try and write it all as one function?
I am quite new to Node.js and already frustrated due to nested callbacks which make it very hard to read the code and troubleshoot for typos.
As you can see below, I have 2 associated models (Blog and Comment) and app.get method which I create Comment for a Blog post.
Model Structure:
Blog
..title (string)
..blog (string)
..comments (Referenced Comment Model)
....comment (string)
Comment
..comment(string)
Currently app.get method has 3 nested call back functions, possible errors are only console.logged yet (for a better user experience if I start to write more codes for errors function becomes real mess).
app.post('/blog/:id/comment',function(req,res){
Comment.create(req.body.comment, function(err, newComment){
if (err) {
console.log(err);
} else {
Blog.findById(req.params.id, function(err, foundBlog){
if (err){
console.log(err);
} else {
foundBlog.comments.push(newComment);
foundBlog.save(function(err, data){
if(err){
console.log(err);
} else {
res.redirect('/blog/'+req.params.id);
}
});
}
});
}
});
});
Here I would like to ask your suggestions to simplify below function and how to better handling errors.
As others have commented, promises is the way to go and async/await is generally the most elegant approach to writing promises. As an example, your code could be condensed to the below. You should read up on promises as they are an important concept for node development.
app.post('/blog/:id/comment', async function(req,res){
try{
const newComment = await Comment.create(req.body.comment);
const foundBlog = await Blog.findById(req.params.id);
foundBlog.comments.push(newComment);
await foundBlog.save();
res.redirect('/blog/'+req.params.id);
}
catch(err){
console.log(err);
}
});
Looks like you are using Mongoose, which supports promises, so you could do something like this:
app.post('/blog/:id/comment',(req,res) {
Comment.create(req.body.comment)
.then(newComment => {
return Blog.findById(req.params.id))
.then(foundBlog => {
foundBlog.comments.push(newComment)
return foundBlog.save()
})
})
.then(() => res.redirect('/blog/' + req.params.id))
.catch(err => console.log(err))
})
You could also use async-await:
app.post('/blog/:id/comment', async (req, res) {
try {
const newComment = await Comment.create(req.body.comment)
const foundBlog = await Blog.findById(req.params.id)
foundBlog.comments.push(newComment)
await foundBlog.save()
res.redirect('/blog/' + req.params.id)
}
catch(err) {
console.log(err)
}
})