Asynchrony
All code is executed by your computer's processor. So far, your code has been synchronous, that is, it used your CPU throughout its execution. For example, the speed of a loop to iterate over an array depends on CPU speed.
There are operations that interact with the outside world. For example, exchanging data with a server over the network, which is much slower than receiving data from memory. If such operations are processed synchronously, the processor stays idle (instead of executing other code) while a network request is being made to the server.
Synchronous code is executed sequentially, with each instruction waiting for the previous one to be completed. When you call a function that performs a long-running action, it stops the program for the entire duration of its execution. That is, in the synchronous programming model, everything happens in turns.
Imagine a queue at the train station. You cannot buy a ticket until the person in front of you has bought it. Likewise, the people behind you cannot start buying tickets until you do.
In asynchronous code, multiple operations can be carried out simultaneously. In this model, a network request to the server will not stop the program, and it will proceed with other operations. When the request has been completed, the program is notified straight away and gets access to the result (for example, data from the server).
Imagine having lunch at a restaurant. You and other visitors order some meals. You do not have to wait for their meals to be delivered before making an order. Likewise, other visitors do not have to wait for you to receive your food and eat before they can order. Everyone will receive their meals as soon as they are cooked.
Let's consider the difference in an example in which the program makes two network requests to the server and then processes their result. Operations 1
and 2
are functions that make requests to the server, whereas 3
, 4
and 5
are any other code you are familiar with.

In the synchronous model, everything is clear and rather sad: previous operations block the execution of subsequent ones until they are over. If operations 3-5 are handling user clicks, the interface will freeze until requests 1-2 are completed and processed.
For example, a user has just posted a comment (network request) and wants to open a sidebar with the latest news. After clicking to submit a comment, the interface will freeze and will not respond to his actions until the result of submitting the comment is received from the server. This is not very convenient.
In the asynchronous model, the start of a network request causes a kind of forking, that is, making a request and getting the result of its processing are different actions. While the request is being executed, the program continues to run and execute other code. After the network request has been completed, the program can start processing its result as soon as it is free. This means that the user will be able to open the sidebar with the latest news immediately after posting his comment, without waiting for a response from the server.
That is, only one operation can be performed per unit of time because JavaScript is single-threaded. Asynchronous programming is made possible through delayed function calls, where initializing an asynchronous operation and processing its result are different actions.
Asynchronous code
In synchronous code, the next instruction cannot start executing until the previous one is completed. That is, instructions are processed sequentially.
console.log("First log");
console.log("Second log");
console.log("Third log");
The following code is asynchronous. You will learn about the setTimeout()
function later. Now all you need to know is that it takes two parameters: a callback that will be called after the time you pass as the second argument.
// Will run first
console.log("First log");
setTimeout(() => {
// Will run last, after 2000 milliseconds
console.log("Second log");
}, 2000);
// Will run second
console.log("Third log");
The setTimeout()
function synchronously executes and registers a delayed call to the passed callback, which will be called asynchronously after the specified interval.
Multithreading
Do not confuse asynchrony and multithreading (concurrency) – they are different programming models. Let's consider a simple analogy that will distinguish these concepts. Imagine that you are a chef in a restaurant and you get an order for coffee and toast.
- Synchronous single-threaded approach: you first make coffee, then toast and serve them, after which you clean up the kitchen.
- Asynchronous single-threaded approach: you start making coffee and set the timer, then start making toast and set the timer the same way. While the coffee and toasts are being prepared, you clean up the kitchen. When the timers go off, you take off the coffee and toast and serve them.
- Multi-threaded approach (concurrency): you hire two assistants, one for making coffee and the other for toast. Now you have the problem of managing your assistants (threads), as you do not want them to conflict with each other when sharing resources in the kitchen.
In asynchronous single-threaded processes, you have a task schedule where some tasks depend on the result of others. After another task has been completed, code is called to handle its result. With that, you only need one worker to complete all tasks, not one worker per task.