Don't make that function async.
José Clovis Ramírez de la Rosa
Posted on July 19, 2018
Today's a good day to write Javascript code. ES2015 and the newest features that are coming to the language every year makes it a joy to use. With ES2017, Javascript gained Async/Await, which gives a more concise, expressive syntax to handle asynchronous code. It is available in the latest releases of Node and it can be transpiled using Babel to target the browser.
However, as awesome as it may be, marking a function as async won't magically make your code more performant. More often than not, I stumble against this type of code:
async function saveUser(userData) {
const user = await this.userRepository.saveUserInDatabase(userData);
return user;
}
Oh, is that type of async function again.
What happens when you await a function.
When using the keyword await
, Javascript will pause the function's execution and return the control to it's caller until the asynchronous operation is completed. In the previous example, our saveUser function will stay standby, waiting for the promise returned by the call to the method saveUserInDatabase(userData)
to regain the control and return the results.
Now imagine that we make every single function async. Every function would have to wait for every individual function to be resolved, then that function will get back the control, just to give it back to that function's caller. Under this circumstance the Node process will have to start/stop and save/recover every function's execution state. This actually makes the code less performant!
Imagine if we have several layers in our system, each waiting for the other one to complete to temporally gain the control and give it back again. Your callstack might end up looking like this:
Advices for making a function async.
Generally speaking, async functions are just a promise. They represent a future value that has not been computed yet (can also be seen as an ongoing event). It is a good practice to delay the Promise's computation as much as possible until the value is needed. I think that an async function is a good idea if:
- You need the value of another async function: For example, you may want to wait at the database layer for a query to complete so that you can wrap the result and transform it into an entity of your domain.
- You need to perform extra steps after the completion of an async function: For example, it would be okay to wait for the user to save in the database if we wanted to send him an email right after. We can return a promise that symbolizes the user email instead.
async function saveUser(userData) {
const user = await this.userRepository.saveUserInDatabase(userData); // We need the user.
return this.sendUserEmail(user); // this.sendUserEmail is another async function.
}
You need to handle the exception: Sometimes you want to run an async operation but you need to respond to an eventual failure at that layer's level. For example, we could await the database layer execution when creating an user if we have to delete the user profile picture on failure.
It makes the code more expressive: There may be a time where we need to make a tradeoff between performance and cleaner code :).
Wrapping up.
Async/Await is one of the best things ever reaching the Javascript world. It is a powerful and expressive way to express asynchronous operations. With all this greatness, overusing it can hurt performance, readability and creates complexity - a complexity that it's just not worth the effort sometimes. When creating functions, it is a good idea to give back the control to the function's caller as fast as possible.
Happy Coding!
Posted on July 19, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.