First, we should understand the context in which our code is executed, or the runtime environment. You can think of this as the abstract container where the browser, the JS engine, web APIs, and your code all interact.
Broadly, the event loop is a process the browser undergoes whenever it’s sent asynchronous code. It handles the timing of the execution of various chunks of the program.
One major part of the event loop is the event listener hash. This is basically a storage object for asynchronous functions to go after they've been parsed by the interpreter. There, they wait to be invoked. How long? Depends. But these functions are not touched until after the interpreter is finished running through the entire code body.
The callback queue is another key part of the event loop. Functions waiting in the event hash are eventually sent to the queue if its associated event has happened (time passing for setTimeout, or a click event, or a server response for an AJAX call, etc)...but again, only after the interpreter is done running through the code.
Each event loop, or tick, has three steps.
- First, the event loop performs some internal upkeep, which could be something like removing expired events from the events listener hash.
- If and only if the callstack is empty, it moves the appropriate functions from the events listener hash to the callback queue.
- The final step is to dequeue the first function from the queue and invoke it. And then the loop starts over gain.
Let's walk through how the above code would run with the event loop and the queue.
First, the engine runs through the code line by line. If it sees any asynchronous functions, it passes them off to the event listener hash. In our example, the functions on lines 6, 10, and 13 all are handled asynchronously (by an event click, a
setTimeout, and an AJAX call respectively), so they go wait in the hash.
The only function that actually gets invoked by the engine is
logMeNow on line 19.
Once the engine is done interpreting, the browser can turn its attention to the event listener hash. It moves whatever functions it can to the callback queue (step two in the event loop). For now, this would include the
phoneCall from line 9, since it had a wait time of 0 milliseconds.
We’re also gonna hypothetically say a user clicked on the body at this point, so we’ll queue up the function from line 6 too. And let's say we haven’t gotten our AJAX response yet from our request on line 13.
The browser has now added everything to the queue that it needs to, so it can move onto step three of the event loop, which is dequeuing and invoking the function at the front of the queue.
phoneCall from the
setTimeout would be dequeued and invoked, and the click event
phoneCall would be pushed to the top of the queue.
The loop then starts over with step one, the browser's internal upkeep. This would include removing the
phoneCall from the hash. (The event click function (lines 5-7) would remain in the hash, though-- click events and similar events remain in the hash throughout the life of a program.)
Hopefully this helped to clear up some confusion you may have had about the event loop. To recap, it's a tool the browser uses to handle asynchronous functions. It does that by storing those async functions in the event listener hash until (1) the interpreter is done running through the code and (2) the callstack is empty. When it's time, it moves functions whose 'events' have occurred from the hash to the queue, where they're invoked one at a time.