- Amit Barletz
Single Threaded, Non-Blocking, Asynchronous, Concurrent Language - huh?! 😵
A continuation of the previous blog post Big Words - Part 1.
As a self-taught programmer, when I had first encountered that statement, the first question I asked was:
'WHAT IS A THREAD?'
Thread in computer science is the execution of running multiple tasks or programs at the same time. Each unit capable of executing code is called a thread.
A call stack is a data structure that records where we are in the program and which execution context is currently active. As a continuous region of memory, primitive types are stored in the call stack, in the execution context in which they were declared.
Object literals, arrays, and functions are stored in the heap, which is a much larger region.
concurrency model is when two or more tasks can start, run, and complete in overlapping periods.
A Deep Look at How It Works Behind The Scenes:
Let's have a look at the following code.
What do you think the output of this code will be?
console.log('1') setTimeout(() => console.log('2'), 2000) console.log('3')
The answer is '1', then '3' and in the end, after two seconds '2'.
The next line of code: 'console.log('3');' is pushed to the call stack, and again, as nothing is excluding that line of code, and it is on the top of the stack, it is executed, popped off the stack and prints '3' to the console.
When the Web API is done with 'setTimeout', it pushes the callback to the callback queue (the callback of 'setTimeout' is the code after the fat arrow). Then comes the important part of the event loop.
The event loop checks again and again if the call stack is empty. If so, it checks if there are any callbacks in the callback queue, and if there are, it pushes the first callback from the queue to the call stack. Then the code is executed, popped off the stack and prints '2' to the console.
Now, to the famous interview question:
what do you think will happen if we change 'setTimeout' to zero seconds?
setTimeout(() => console.log('2'), 0)
It still gives us the same output because even though it zero seconds, it still went through the process.
Every piece of code that is a part of the Web API, went through the same process explained above. In other words, if we had a DOM event, 'fetch' or 'AJAX' request instead of (or in addition to) 'setTimeout', it would have to go through the process of
Web API → callback queue → event loop → call stack
Spoiler Alert! - Now it is No longer accurate
What do you think will be the output of the following code?
console.log('1') setTimeout(() => console.log('2'), 1500) fetch('https://api.jsonapi.co/rest/v1/user/list') .then((response) => response.json()) .then((responseJSON) => console.log(responseJSON.data.users)) console.log('3')
There is an interesting thing about the code example above.
The output of the code above is 1, 3, the list of users and 2. The user's list response from the JSON API is printed before the timer, even though it may take more than 1500ms to return the response.
Inside 'fetch()' we have '.then()', which is basically a callback, but it is a callback related to Promises. Callbacks of promises are not going to the callback queue, instead, it has a special queue called microtasks queue, which has a priority over the callback queue.
How does it happen?
After a callback has been taken from the callback queue, the event loop will check if there are any callbacks in the microtasks queue, and if there are, the event loop will run all the microtasks queue callbacks before it runs any more callbacks from the callback queue. Moreover, if one microtask adds a new microtask, then that new microtask is also executed before any callbacks from the callback queue.
That's why the duration we defined fro 'Timers' ('setTimeout', which is sent to the callback queue) is not guaranteed, the only guarantee is that the timer will not run before the time we had defined, but it might run after that time. It all depends on the state of the callback queue (also called Macrotask), and the microtasks queue.
So, with that knowledge, can you guess what will be the output of the following code? (answer is at the end of this post 🤫).
setTimeout(() => console.log('1'), 0) setTimeout(() => console.log('2'), 1000) Promise.resolve('ho').then((data) => console.log('hi', data)) console.log('3')
Thanks for reading, that was a pretty short blog post, relatively to the others. I hope it was short, concise but illuminating.
And of course, it isn't a good blog post about the event loop without referencing Philip Roberts's talk:
What the heck is the event loop anyway?
There are 2 other talks, more recent and updated (microtasks queue), which I recommend watching:
Further Adventures of the Event Loop - Erin Zimmer
In The Loop - Jake Archibald:
By the way, the output of the code above would be 3, hi ho, 1, 2.