So we all know the definition of JavaScript, it is a single threaded synchronous language.
This means it has one call stack and one memory heap, it executes code in order and must finish executing a piece of code before moving onto the next and hence the language is blocking in nature.
Again JavaScript is synchronous and single-threaded so if we execute a JavaScript block of code on a page then no other JavaScript code on that same page will parallelly be executed!
The definitions seems absurd, since we all use asynchronous stuff using JS or is it all an illusion?
so from the various resources I've read on this topic, here's what I understand:
JavaScript itself is synchronous but the browser makes it possible to code in an asynchronous way, How is that?
Well the answer is :
by using the Browser APIs, such as Fetch API, setTimeout, Promises, Geolocation API etc ..
Whenever an asynchronous function is called, it is sent to a browser API, these are APIs built into the browser.
Based on the commands received from the call stack, the API starts its own single-threaded operation.
An example of this is the setTimeout method.
When a setTimeout operation is processed in the call stack, it is sent to the corresponding API which waits till the specified time to send this operation back in for processing.
Where does it send the operation? The event queue.
The event loop constantly checks whether or not the call stack is empty, if it is empty, new functions are added from the event queue, if it is not, then the current function call is processed.
So lets dive deeper into the event queue itself.
To make sense out of all of this, we need to talk about some terminology first:
- Tasks : any block of code (literally)
- Browser APIs : APIs that usually start their own threads to run code parallelly ( most of them requires a callback which will later on be pushed to the event queue )
- Micro Tasks : any callback related to a promise
- rendering process : anything related to rendering such as style calculation, or any requestAnimationFrame callback
- call stack : a mechanism for an interpreter to keep track of its place in a script that calls multiple functions ( what function is currently being run and what function initially called it ).
- Heap : an area of pre-reserved computer main storage ( memory ) that a program process can use to store data
- Thread : a sequence of programmed instructions that can be managed independently by a scheduler it got its own heap and stack.
Alright time to make sense of it all!
So we said that the main thread runs JavaScript synchronously, when we use some certain BrowserAPI when that command is executed in the stack a new thread is initiated that runs its code independently
Letโs take setTimeout as an example, the new thread will keep tracking sysTick till the X ms runs out, the new thread sends a message to the main Thread telling it to enqueue (push) its attached callback to the event queue, the event loop then waits till the call stack is empty to dequeue some callback into the stack, which will then be executed.
The Scheme explains it perfectly.
What is important to note is that not all callbacks got the same priority and the same order of execution or enqueueing.
A funny representation that I saw in JSconf presentation, describes the event loop as following:
while(true){
var Queue = getNextQueue();
var Task = queue.pop();
Execute(task);
while(microtaskQueue.hasTasks){
doMicrotask();
}
if(isRepaintTime()){
animationTasks = animationQueue.copyTasks();
for ( task in animationTasks)
doAnimationTask(task);
repaint();
}
}
MicroTasks as many sources explains them, are usually promises callbacks, please notice that when pushing them to the event queue we push the entire MicroTask queue, while when pushing Tasks we only push the first-in callback in the tasks queue.
We also push the entire render queue to the event queue when it's time to render ( usually browsers repaints the screen every 16/17ms since most of them runs with a frequency of 60Hz )
So a good practice is to use requestAnimationFrame to run animations rather than running it in simple tasks or microtasks, since its pointless to repaint it in higher frequency cause the the human eye can see between 30 and 60 frames per second (30/60Hz).
Another presentation in the JSConf visualizes the event loop as following
So as a conclusion:
The event loop is the secret behind JavaScript's asynchronous programming. JavaScript executes all operations on a single thread, but using a few smart data structures, it gives us the illusion of multi-threading, so describing JavaScript as asynchronous is arguably misleading.
I hope this made sense in anyway, if not I highly recommend you to check these presentations to watch, I guaranty you will understand it much better:
Please feel free to explain it in your own way in the comments section or to provide us with more links about the subject.
Thanks for reading.
Top comments (1)
Thank you so much!! This is the best diagram and the most accurate description iโve ever seen:)