Event Loop is one of the most misunderstood concepts in Javascript. Since javascript is single threaded, it is still able to perform concurrent and async operations. Such operations are traditionally performed using threads in Ruby or Java systems. Through this post I wish to clear out some of the doubts on how Javascript performs async operations.
Event loop has been the key concept in non blocking I/O which has found its way from the browser to server in the form of Node.js. In order to understand event loop, it is important to understand how Javascript runs in general.
Call stack
When javascript runs a piece of code, it keeps reference of the current executing function in a stack which is called the call stack. When the function finishes execution it is removed from stack.
In the above example we have three functions (fn1, fn2, fn3). Following is the sequence of steps when javascript executes the code:
- Start execution using the main thread referred to as main() here.
- Identify the function which needs to executed and add it in the call stack.
- Identify other function which the current function executes and add it in the call stack.
- Upon completion of the function remove its reference from the call stack.
- Repeat steps #2 to #4 till the code finishes execution.
- After all the code has finished execution, remove main() from the call stack.
We can see call stack in action in browser when we get a javascript error.
Here browser shows the call stack which lead to javascript error. This allows us to debug the code.
Single thread
Javascript doesn’t have the concept of threads. It has a single thread which executes code in sequential manner. It means that any function which takes a lot of time can block thread execution. When thread execution is blocked, browser cannot perform any function such as take user inputs. Essentially the page freezes. However, any non trivial application has computation intensive code. So there has to be a mechanism to handle such operations.
Async call stack
To solve this we write asynchronous code which performs time consuming operations in the background without blocking the main thread and uses callback mechanism to resume code execution. setTimeout
is one such mechanism.
setTimeout
is one of the mechanism to write async code in Javascript. In the flow above, we have a setTimeout
which will run the function passed to it after at least 5 seconds. The main thread executes setTimeout
sequentially just like any other function call. Hence, the call the stack is similar to our previous example. However, after 5 seconds, the setTimeout’s callback magically comes into picture and gets added in the call stack. It means that Javascript wasn’t just executing the actual code, it was also keeping track of when to call the setTimeout callback. Clearly, this violates our assumption that Javascript being single threaded can do only one thing at a time. There is more to it that meets the eye. The magic referred to here is performed by the event loop.
Event loop
When we say Javascript is single threaded, we are referring to the Javascript runtime (for eg. V8, Blink). The event loop is what browsers provide. Following is the basic anatomy of a browser:
- Javascript Engine: Javascript engines like V8, Blink provide the javascript runtime. It contains the function call stack and memory heap. Memory heap is used to maintain references to objects and functions required by call stack.
- WebAPIs: APIs like setTimeout, setTimeInterval are provided by the browser. These APIs are responsible to add callbacks in event queue. For eg.
setTimeout(cb, 5000)
will add the callback functioncb
in event queue after the 5 seconds. - Event Queue: Event queue contains functions which are to be added in call stack by event loop.
- Event Loop: Event loop is responsible to push the functions available in event queue to the main call stack. Event loop pushes a function only when the call stack is empty.
Important thing to note here is that event loop will only add a call back from the event queue when it finds the call stack as empty. If Javascript’s main thread is busy executing a long running function, it would keep the callback in queue. The broader implication of this is that if we have long running functions which block the main thread, Javascript will not be able to respond to user events as they would be stuck in event queue.
Conclusion
Event loop is browser’s mechanism to perform non blocking operations. It does that by providing webAPIs (eg. setTimeout) which are capable of maintaining callback references in memory. The callbacks are added to event queue depending on the logic of the webAPI (for eg. setTimeout waits for n milliseconds, XMLHTTPRequest waits for ajax call to finish). Event loop adds callback from the event queue and pushes it to the call stack where the callback is executed.