The JavaScript EventLoop
Tushar Goel
Lead Member Of Technical Staff @ Salesforce | Staff Software Engineer, Technical Lead
This post will explain one of the most important concepts of JavaScript i.e. EventLoop.
Your JavaScript code runs single-threaded. There is just one thing happening at a time. This will simplify how you program without worrying about the concurrency issues.
In general, in most browsers there is an event loop for every browser tab, to make every process isolated and avoid a web page with infinite loops or heavy processing to block your entire browser.
A function calls to form a stack of frames.
Heap is the memory where objects reside.
A queue is the message queue. A JavaScript runtime uses a message queue, which is a list of messages to be processed. Each message has an associated function that gets called in order to handle the message.
Let's understand by an example:
const add = (a, b = 5) => a + b; const multiply = (x, y = 3) => add (x * y); console.log(multiply (5)) //returns 20
When this code runs, first multiply(5) is called. Inside multiply() we first call add(). At this point the call stack looks like this:
The event loop on every iteration looks if there’s something in the call stack, and executes it
until the call stack is empty.
The event loop got its name because of how it's usually implemented, which usually resembles:
while (queue.waitForMessage()) { queue.processNextMessage() }
queue.waitForMessage() waits synchronously for a message to arrive (if one is not already available and waiting to be handled).
Queuing function execution
Messages are added anytime an event occurs and there is an event listener attached to it. If there is no listener, the event is lost. So a click on an element with a click event handler will add a message—likewise with any other event.
Let's take another example using timeout and observe the behavior.
const bar = () => console.log('bar'); const baz = () => console.log('baz'); const foo = () => { console.log('foo'); setTimeout(bar, 0); baz(); } foo();
This code prints, maybe surprisingly:
foo baz bar
When this code runs, first foo() is called. Inside foo() we first call setTimeout, passing bar as an argument, and we instruct it to run immediately as fast as it can, passing 0 as the timer. Then we call baz().
Why is this happening?
The Message Queue
When setTimeout() is called, the Browser or Node.js start the timer. Once the timer expires, in this case immediately as we put 0 as the timeout, the callback function is put in the Message Queue.
The Message Queue is also where user-initiated events like click or keyboard events or any other events.
The loop gives priority to the call stack, and it first processes everything it finds in the call stack, and once there’s nothing in there, it goes to pick up things in the message queue.
We don’t have to wait for functions like setTimeout, fetch or other things to do their own work, because they are provided by the browser, and they live on their own threads. For example, if you set the setTimeout timeout to 5 seconds, you don’t have to wait 5 seconds - the wait happens elsewhere.
Hope it explains how event loops work.