Learning the Event Loop


Second post, and a slightly long one about something I've spent some time learning about - The Event Loop. The below is basically an exercise for me to internalise what I've learnt. If I've got any of it dramatically wrong then please do email or tweet me!

JavaScript runs on a single 'thread' and can only perform one action at a time, and these actions are queued up and managed by the event loop, a sort of task manager.

The most useful video I found for this was this JSConf talk by Philip Roberts, and of course check out the great MDN documentation.

A drawing of the event loop

The drawing above was done with Excalidraw, a cool drawing tool I found recently.

Anyway, JS executes functions from an array called the call stack. Functions are added to the call stack when called from their environment ('the heap') by the event loop. They're then executed in the order in which they're called, like a last in, first out queue. So a function call on line one of your code will be added and executed by the call stack before a function call on line two. In the diagram above, main() is the program context and is always first on the stack.


In this example, the call stack adds console.log('Hi'), then executes it and removes it from the stack, then adds console.log('Hello') and executes it and removes it, and so on.

This is what enables callbacks, as JS works line-by-line through each function that is executed. A callback function is added onto the call stack on top of the intitial function until it's executed and removed, and we can move onto the next function. I imagine the call stack in action looking like a waveform as it undulates up and down, or a stack of plates in a busy kitchen being added to and taken from.

This is fine, until you try to execute a function that takes a long time to run. Image processing or a network request are common examples. This blocks the next function from being executed until the current one is completed, and can mean that a web page or program locks up or hangs until is completed (if ever).

The message queue

Luckily, the browser and Node.js provide APIs for this eventuality. There are a number of event-orientated functions given to us by the browser and Node that allow functions to execute in the background (they run in their own threads), thereby not blocking the call stack. Any callback functions in these APIs are bumped to the message queue, which is first in, first out. If the call stack is empty, then the event loop will look at the message queue and begin moving those functions into the call stack to be executed.

Good examples are event listeners and setTimeout, which do something / wait in the background for something to happen before passing a callback function to the message queue.

Pretty cool! Working on this gave me one of those fuzzy moments where things slide into place in your mind, and it sets the stage for the job queue, Promises and Async / Await, which I'll get into in a future post.

More posts