Let’s summarize the most common misconceptions and clarify them.
A blocking code means that if the main thread is occupied with a certain task, all the other “todo’s” will be put to the side. From a user experience point of view, this is something we want to avoid, because a blocking task will obviously prohibit all kinds of user interaction.
And what about the aforementioned asynchronous code?
Asynchronous codes are usually run after an event, like a network response, or after a certain delay. In a nutshell: the JS engine adds these so-called “callbacks” (all callbacks, promises, and micro tasks) to certain event queues (promises and micro tasks have separate queues), then runs them after processing the current functions on the main thread.
Asynchronous code is a great tool to wait for events without blocking the main thread. For example, if we use asynchronous code, our functions will not be blocked by waiting for a network response, rather the callback function will process the response as soon as it arrives.
However, please note that long-running tasks will block any main threads, regardless if they are executed regularly or as a callback.
Let’s see some examples of the user experience blocking scenario. In the following example, you will see two rotating text items: CSS manages the rotation of one of them, which will be unaffected by the blocking code because the browser runs it totally separately. A script rotates the other one and a simple “setTimeout” callback modifies the angle of the text every 100 ms. You will see that this rotation script will be blocked when the main thread becomes occupied by a long running operation started by user activity.
There are several ways to run asynchronous code, but they will still block your main thread and thus, hamper user experience as well.
- async function
- setTimeout callback
In the following example, we ran the same long running task with async function, and with a “setTimeout” callback — both seem to be blocking…
Generator functions can be used to “fake” parallel operations, but a long running operation will still block them, and this way the whole single threaded operation.
However, there is a way to truly run your computation heavy functions in parallel to each other (essentially on separate processor threads).
It’s possible by using the web worker API of modern web browsers, and also possible in node.js as well, but with a different API.
Now, let’s see in our final example if we are still blocking the UI… and no!
Wrap it up
When you run asynchronous code, the engine only executes the function when a certain event occurs (like a server response) or when it finishes its current tasks — like the (in)famous setTimeout with 0 sec delay.
When you’re faced with the need for a long-running operation, it’s better to set it up in a separate thread or to split it up into chunks. This way, you will be able to keep your user interface’s response time low.