Naveera A.
—How do callbacks work in JavaScript?
Let’s say we have the following code:
function first() { console.log('First'); } function second() { console.log('Second'); } function third() { console.log('Third'); }
We call the functions in order like so:
first(); second(); third();
And we get the following output:
First Second Third
Let’s add a setTimeout
method in the second function, like so:
function first() { console.log('First'); } function second() { setTimeout(() => { console.log('Second'); }, 0); } function third() { console.log('Third'); }
Now if we call the functions in order as before, we get a different result:
First Third Second
Why does the second
function run after the third
function, even though the timer in the setTimeout
function is set to 0?
To understand why we are getting the unexpected output, we first need to understand the JavaScript event loop.
Since JavaScript is a single-threaded programming language, it can only process one statement at a time. To handle the asynchronous operations, like the setTimeout()
method, JavaScript uses a concept called the event loop.
According to the documentation:
JavaScript has a runtime model based on an event loop, which is responsible for executing the code, collecting and processing events, and executing queued sub-tasks.
The event loop keeps track of the order of execution using a stack and a message queue.
You can think of the stack as the execution area, where each function is added in order. The queue is a waiting area for the asynchronous functions.
Let’s take a high-level look at how the event loop will execute our three functions.
For the three functions without any asynchronous code, the browser will do the following:
Add first()
to the stack.
first()
which will log First
to the console.first()
from the stack.Add second()
to the stack.
second()
which will log Second
to the console.second()
from the stack.Add third()
to the stack.
third()
which will log Third
to the console.third()
from the stack.Check the queue for messages (functions waiting to be processed).
Execution cycle complete.
And we will get the following output, as expected:
First Second Third
When we add the setTimeout()
method to the second function, our execution steps look like the following:
Add first()
to the stack.
first()
which will log First
to the console.first()
from the stack.Add second()
to the stack.
second()
setTimeout()
to the stack on top of second()
.setTimeout
API, which will start a timer. At the end of the timer, the anonymous function will be added to the message queue.setTimeout()
from the stack.second()
from the stack.Add third()
to the stack.
third()
which will log Third
to the console.third()
from the stack.Check the queue for messages.
setTimeout()
Add the anonymous function to the stack.
Second
to the console.Check the queue again for messages.
Execution cycle complete.
And we will get the following output:
First Third Second
The important thing to remember is that the timer in the setTimeout()
method does not set the time after which the code will execute. It sets the time after which the event loop will add the anonymous function to the queue.
If we were working with an external web API, the anonymous function would be added to the queue after the API has returned its data.
The timer, or any other asynchronous code, cannot add the anonymous function directly to the stack at the end of its completion as it would interrupt the currently running function.
And the event loop will execute any function in the message queue only after executing all the top-level functions.
If we want to delay the execution of the third
function until the second
function has been completed, we can use the third
function as a callback to the second
function by passing it as an argument to the second
function, like so:
function first() { console.log('First'); } function second(cb) { setTimeout(() => { console.log('Second'); // Execute the callback function cb(); }, 0); } function third() { console.log('Third'); } first(); second(third);
When we run the above code, we will get the desired output:
First Second Third
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.
Here’s a quick look at how Sentry handles your personal information (PII).
×We collect PII about people browsing our website, users of the Sentry service, prospective customers, and people who otherwise interact with us.
What if my PII is included in data sent to Sentry by a Sentry customer (e.g., someone using Sentry to monitor their app)? In this case you have to contact the Sentry customer (e.g., the maker of the app). We do not control the data that is sent to us through the Sentry service for the purposes of application monitoring.
Am I included?We may disclose your PII to the following type of recipients:
You may have the following rights related to your PII:
If you have any questions or concerns about your privacy at Sentry, please email us at compliance@sentry.io.
If you are a California resident, see our Supplemental notice.