Neither async nor sync are better in Javascript. Rather, they both have specific use cases. This post will show you the most common cases for each and the real world scenarios in which you would use them.
TLDR; Use async code for all long running processes that take longer than 30 milliseconds. You can use synchronous code for everything else.
What is Synchronous Code?
This is code that executes in the order it is written. Take this example, where 3 tasks happen in order:
Task sequence: A -> B -> C
Therefore task B cannot start until A is finished and C cannot start until both A and B are finished. Synchronous code is normally where beginners to Javascript will start as it’s easy to write and understand.
This is how you might write it in Javascript:
let A = fetchData(myVariable)
let B = fetchData(A)
let C = fetchData(B)
The Javascript interpreter reads your code line by line, executing everything from left to right and top to bottom. It does not move on until a line has finished executing.
What is Asynchronous Code?
Async code allows for tasks to happen at the same time. In our example tasks A, B and C could all happen in parallel, assuming they did not rely on each other for a result.
Tasks happen at the same time: A & B & C
However, if task B and C depend on A then you cannot run them all at the same time! In that case you might think you need to revert to synchronous code, but that’s not actually necessary. You should still use async code because you don’t want to tie up your main worker thread with long running tasks.
This ability to use asynchronous code in a synchronous manner is where the confusion lies for most new coders.
Keeping Your JS App Responsive
Let’s say you have task C which relies on output from B, which relies on A. Clearly you would write these:
Run tasks: A -> B -> C
This looks exactly the same as our synchronous code example earlier. However, what if each task takes 2 seconds? That’s 6 seconds where your app is non-responsive, which is awful UX design. In fact Android will force close your app if you go over 15 seconds!
This is because Javascript usually runs on the “UI or display Thread”. This is the CPU thread that conducts the orchestra of what’s happening in your code. It should be kept free of long running tasks, so it can concentrate on orchestration.
Analogy: Imagine if an orchestra conductor had to conduct everyone, play the flute, guitar, piano and why not throw in sweeping the floor. Impossible right? And that’s why you keep long running tasks off your UI thread.
If you’ve ever experienced an app crash (app not responding error) then it’s very likely that the developer wrote long running synchronous code on the UI thread!
Async Code with Await / Promises
Therefore, for long tasks, we have to take what should be straightforward synchronous code but use it in an asynchronous manner! This tells the system running your app to offload the task to a different CPU thread and please “callback” later with the answer.
At some point in the future the answer comes back and you can use the result. This is what a callback looks like:
getMyData((result) => {
//this is the callback section
process(result)
})
However callbacks can get very messy if you have a highly functional app. This is what happens if you have tasks A, B and C all waiting for each other:
doSomething(a, (b) => {
doSomething(b, (c) => {
doSomething(c, (finalResult) => {
ARE WE FINISHED YET??!?
})
})
})
The above code, ladies and gentlemen, is called callback hell. Imagine 10 processes with multiple steps between them and those nested callbacks can become insane sea monsters that will cause you to smash up your work cubicle!
Callback hell is why the concept of async & await was invented.
Javascript – Async and Await
Async await is doing exactly the same thing as those callbacks earlier, except you write the whole callback in one line so it looks synchronous code, making for much easier reading.
Let’s go back to the example with long running tasks A, B, C. Each must wait for the one before so it can now be written:
let resultA = await fetchData(myVariable)
let resultB = await fetchData(resultA)
let resultC = await fetchData(resultB)
The advantage to writing async code this way is that all that processing and waiting is now kept off the main UI thread. Therefore your app remains responsive. Coding like this is very, very important on the server side as you always want your server to be reachable.
You can also click this if you want to know how async is related to promises.
Is Async Code better than Sync?
Yes and no. Async code should be used for long running or intensive tasks or ones where you do not know how long it will take. For small things such as storing variables or changing simple UI elements then you are perfectly fine with synchronous code.
The above recommendation comes from understanding the frame rate of applications. Generally a user interface is considered smooth at 30 frames per second. Therefore you need to ensure that the UI thread is free every 1 second divided by 30 frames. That equates to a frame draw every 30 milliseconds.
If your task goes over this 30ms threshold then make it async! An easy way to test this is to run your app for real, triggering multiple calls of the synchronous function that you’re concerned about. If the UI stays smooth then you’re good to go, subject to the below chapter on when to use async.
You can also test how long certain functions take by opening the console on your browser (Chrome or Safari) and using the timelines functionality.
When to Use Async Javascript
As a rule of thumb you would always use async in the following situations:
- Making a network call to a server or API
- Processing something heavy (large data file or image)
- Making a database call for reads or writes
In fact, as you progress in your coding skill you’ll notice that more and more libraries use await async. Therefore it’s best if you get familiar with these concepts now, as it’s also encountered in many other programming languages and frameworks.
Wrap Up
Async code offloads work onto another CPU core / thread, keeping the main one free for user interactions or server responsiveness. You should always use async code where user experience and responsiveness matters, keeping those long running and unknown length tasks in the background.
Finally, if you want a very deep dive on this subject then have a look at my ultimate guide on asynchronous vs synchronous in Javascript.