Find related posts...
Powered by Algolia Log in Create account
İnanç Akduvan
Follow
I am a frontend developer.
LOCATION
İzmir / Turkey
WORK
Frontend Developer
JOINED
Nov 10, 2020
More from İnanç Akduvan
İnanç Akduvan
Posted on Sep 28, 2022
I created my own React state management
library
106
#react #javascript #redux #zustand
Let me understand how Observer - JavaScript Design Patterns
#javascript #webdev #designpatterns #frontend
JavaScript works under the Singleton - JavaScript Design Patterns
hood #javascript #designpatterns #frontend #webdev
#javascript #webdev #programming
MongoDB PROMOTED
Did you ever wonder what's going on behind the scenes when you run a very small
piece of code in JavaScript? I actually didn't for a long time.
I wanna understand it with you right now. Let's try to discover together! Hop on the
bus, starting! 🚎
I offer to write a very very small code block and try to understand its story:
Build gen AI apps that run
const totalApples = 10;
anywhere with MongoDB
const totalBananas = 5; Atlas
MongoDB Atlas bundles vector search and a
function getTotalFruits() { flexible document model so developers can
return totalApples + totalBananas; build, scale, and run gen AI apps without
} juggling multiple databases. From LLM to
semantic search, Atlas streamlines AI
const totalFruits = getTotalFruits(); architecture. Start free today.
Start Free
Cool, we all know that result is 15 here. So easy. But what happened under the
hood?
Before we start, keep in mind that JavaScript is single threaded and
synchronous language:
It can only execute one thing at a time.
It executes them line by line from top to bottom.
106 FIRST OF ALL, Global Execution Context is created by JavaScript Engine.
22
JavaScript Engine
159
Javascript Engine is a program that executes the javascript code.
Execution Context
Imagine that: Execution context is a two-sided place where JS code is declared
and executed. Imagine a board which has two sides.
On the right side (Memory), all variables and functions are declared.
On the left side (Thread of Execution), code is executed line by line.
There are 2 types of Execution Context:
Global Execution Context (GEC)
Whenever JS Engine executes a script, it creates a global execution context which
all global code handled.
Function Execution Context (FEC)
Whenever a function is called, it creates a function execution context which has its
own local memory and thread of execution.
LET'S TRY TO VISUALIZE:
Remember our code:
// Step 1
const totalApples = 10;
const totalBananas = 5;
// Step 2
function getTotalFruits() {
return totalApples + totalBananas;
}
// Step 3
const totalFruits = getTotalFruits();
Script executed, and Global Execution Context created:
Step 1:
Declare totalApples and totalBananas variables in the memory.
Step 2:
Declare getTotalFruits function in the memory.
Did you realized you can also save a function in the memory? Let's go on.
Step 3 (Part 1):
Declare totalFruits variable in the memory. But do I know value of this variable?
NO! We cannot save commands in the memory. So it will be saved as uninitialized
at the moment. See it:
Step 3 (Part 2):
Time to execute our function on Thread of Execution. AND FOR THE FIRST TIME
HERE, Function Execution Context comes into our live. Here it is:
Is that all? NO my friend! Something is missing here. A new term reveals in this
step. Whenever you call a function, it pops in Call Stack.
Call stack
Call Stack is where the JavaScript Engine keeps track of where your code is in the
execution.
You can basically see which functions is running right now in the Call Stack.
🎤 Imagine that you are a singer. Announcer is calling your name. You are
coming and singing your song. When the song is over, you are getting off the
stage. Stage is Call Stack here. 🎤
When a function is called, it pops in Call Stack. And it pops out when it has done its
job.
Global Execution is running at the bottom as default in Call Stack.
Function called:
Function finished its job:
Note that: There is ONLY ONE Call Stack since JavaScript is a single threaded
language.
HUH! I guess it is done, right? Please warn me, if I do anything wrong. Let's keep
up the work.
It looks okay like this, but, what if we add some complexity to our function?
Let's not be afraid and do it!
It looks very clean when it goes synchronously. But what will happen when some
functions are asynchronous?
We can basically do it with setTimeout:
const totalApples = 10;
const totalBananas = 5;
function getTotalFruits() {
return totalApples + totalBananas;
}
function runTimer() {
[Link]('timeout run!')
}
const totalFruits = getTotalFruits();
setTimeout(runTimer, 0);
[Link](totalFruits);
So guys, here is interesting. New terms reveal here again.
Did you know that JavaScript doesn't have a timer? Also console?
Everybody sometimes needs a support, so does Javascript. WEB APIs help
JavaScript here.
Web APIs
Web APIs are generally used with JavaScript and add some power to our
functionality. Timer, Console, Network requests and many other things are Web
APIs' skills, not Javascript.
JavaScript just communicates with browser via some labels like setTimeout.
Let's Visualize
Assume that, it is setTimeout's turn in the Thread of Execution and let's try to
visualize this step:
hmm, weird things are happening. Let me try to summarize it.
Please some attention now:
Functions and variables saved in memory as usual. They are executed in the
thread. And when it is setTimeout's turn, JavaScript didn't send setTimeout's
callback (runTimer) into Call Stack. Instead, it communicated with Web Browser
and sent a message like:
''Hey buddy 👋 Can you start a timer for me? And when timer is completed, here is
your callback to run (runTimer).''
As you see, after 0ms, timer is completed, callback is ready to run BUT guys,
Thread of Execution never stops, exactly like life.
In the time when Web Browser handles timer, functions kept executing in the
thread and even if callback(runTimer) is ready to be executed, Call Stack is not
empty anymore! And remember: There can be only one execution at a time. BUT I
HAVE TWO FUNCTIONS TO RUN IN MY HANDS.
What is gonna happen? Is JavaScript broken?!?!!?
Please no, we need it.
Or will JavaScript run my functions in a random order?
That would be very unpredictable and disaster.
There should be a better solution.
🚨 NEW TERM ALERT 🚨
Our callback is waiting in Callback Queue.
Callback Queue
Callback Queue is kind of a waiting room for callbacks which are ready to be
executed. Callback is waiting here for the Call Stack to empty. Whenever is Call
Stack is empty, our callback pops in Call Stack and do its job.
Event Loop
Event Loops basically checks Call Stack continuously, and when Call Stack is
empty, let there know about it. And first callback in the queue pops in Call Stack.
Let's add some visuals including Callback Queue
and Event Loop
Oh guys, victory! 🏆
Just a little question; What if current execution continues for 1000 seconds or
whatever while our callback is waiting in Callback Queue? Is it gonna wait forever?
Sounds unbelievable but yes! Whatever the cost, it is gonna wait until Call Stack is
empty. No permission to run unless Call Stack is empty.
Okay! Adventure of this code block completed here, right? But I feel like we can
add some more complexity here.
PLEASE DON'T LEAVE ME! I PROMISE THIS IS LAST SECTION. You will not
regret, be patient!
What would happen if I need to fetch data from server?
Let's fetch some data:
const totalApples = 10;
const totalBananas = 5;
function getTotalFruits() {
return totalApples + totalBananas;
}
function runTimer() {
[Link]('timeout run!')
}
function showData(data) {
[Link](data)
}
const totalFruits = getTotalFruits();
setTimeout(runTimer, 0);
// Fetch some data
const fetchData = fetch('[Link]
[Link](showData);
[Link](totalFruits);
Okay, when we fetch data, we will communicate with Web Browser again. Because
we need its APIs here.
I will not mention about details of fetch API, just talk about what is going on in the
Thread of Execution, Call Stack and Queue while fetching data.
Hmm, let's try to write down a timesheet about what happened here:
1) Functions and variables saved in the memory, executed in the thread blablabla...
2) setTimeout communicated with Web Browser's timer.
3) setTimeout's callback (runTimer) is ready and waiting in Callback Queue after
0ms. [0ms]
4) fetch is doing two jobs:
a) Communicated with Web Browser's network feature.
b) Created a special Promise Object in the memory via Javascript Engine.
5) fetch's callback (showData) is ready with data to run after 200ms BUT we don't
know yet where it is waiting right now. We'll see. [200ms]
6) While all those are happening, [Link](15) popped in Call Stack. AND
assume that: it has taken 250ms to console and after 250ms it will pop out from
Call Stack. (I know it wouldn't take that time in real life but just an assumption to
understand the story). [250ms]
So, [Link](15) is running in the Call Stack, runTimer is waiting in Callback
Queue. Where is showData waiting?
Based on our current knowledge, showData should be waiting right behind
runTimer in the Callback Queue, right?
No my friend, it is not.
For the last time,🚨New Term Alert🚨:
There is an another kind of queue here besides Callback Queue.
Microtask Queue
Whenever a callback is related to Promise Object in JavaScript Engine, it will be
waiting in Microtask Queue such as callbacks of fetch functions.
Note that: Microtask Queue has a privilege over Callback Queue.
First, callbacks in Microtask Queue will pop in Call Stack. After they are done,
callbacks in Callback Queue will start to pop in Call Stack.
In our scenario, firstly, showData will run because it is waiting in Microtask Queue.
And then, runTimer which is waiting in Callback Queue will run.
Then, final result looks like:
What an adventure! 😓 just a fucking piece of code, crazy.
Our journey ends here, guys. 🚎 thank you for joining me!
Stay in touch with me for more adventures 👇
Follow me on:
Github: [Link]
Twitter: [Link]
Thank you for coming with me until end of the reading! 🙂
Bright Data PROMOTED
SOC-CERT: Automated Threat Intelligence System
with n8n & AI
Check out this submission for the AI Agents Challenge powered by n8n and Bright
Data.
Read more →
Top comments (22) Subscribe
Add to the discussion
Gaurav Saini • Sep 30 '22 • Edited on Sep 30
Very nice explanation!
One small addition that I'd like to make is "Hoisting". Even before the code
execution begins, all variable and function declarations are hoisted (in simpler
terms, moved) to the top of the file.
Looking at your very first example, if we make a small change to the code, like so
const totalApples = 10;
const totalBananas = 5;
const totalFruits = getTotalFruits();
function getTotalFruits() {
return totalApples + totalBananas;
}
Even though it may seem a bit counter-intuitive, the code still runs exactly as
before. This is because the function declaration is moved to the top of the file
before execution. Note that only declarations are hoisted, and not definitions, so if
you're assigning any values to some variables, the assignments still stay in the
place where you wrote them, it's just that the declaration statement with an
undefined value is moved to the top of the file.
Speaking of declarations vs. definitions, one little caveat that has caught me off
guard in the past, is with arrow functions. In the above code getTotalFruits is a
function, since it is declared with the function keyword, but if we change it to an
arrow function,
const getTotalFruits = () => {
return totalApples + totalBananas;
};
the code throws a ReferenceError . This is because, getTotalFruits is now
technically a regular variable which is assigned an anonymous function.
Hope you and anyone reading find this useful.
Cheers!
12 likes Reply
İnanç Akduvan • Sep 30 '22 • Edited on Sep 30
Very useful addition, Gaurav! You enlightened us. Definitely I benefit from your
explanation.
Thank you for your contribution 🤟
3 likes Reply
Gnio • Oct 4 '22
Genius
2 likes Reply
Matthijs Wensveen • Oct 5 '22
Very cool, thanks!
Question: what if we put the [Link](..) statement before [Link](..) ?
The call stack is empty after [Link] and even though the fetchData promise is
already done, we haven't put showData on the microtask stack, so runTimer will be
first?
2 likes Reply
İnanç Akduvan • Oct 5 '22 • Edited on Oct 5
Hi, thanks!
If I understand the question, I will try to explain.
Settimeout, [Link], fetch... this is the calling order in your question.
First Scenerio: Fetching takes 200ms time and consoling takes 250ms:
-- setTimeout communicates with Web API
-- [Link] is in Call Stack. [0ms]
-- runTimer is ready waiting in Callback Queue [0ms]
-- fetch communicates with Web aPI [1ms]
-- showData is ready waiting in Microtask Queue [200ms]
-- [Link] run and consoled and removed from Call Stack [250ms]
-- showData run in Call Stack [251ms]
-- runTimer run in Call Stack [252ms]
Second Scenerio: Fetching takes 300ms time and consoling takes 250ms:
-- setTimeout communicates with Web API
-- [Link] is in Call Stack. [0ms]
-- runTimer is ready waiting in Callback Queue [0ms]
-- fetch communicates with Web aPI [1ms]
-- [Link] run and consoled and removed from Call Stack [250ms]
-- runTimer run in Call Stack [251ms]
-- showData is ready waiting in Microtask Queue [300ms]
-- showData run in Call Stack [301ms]
I guess, it is something like that. :)
1 like Reply
Matthijs Wensveen • Oct 6 '22
Interesting. The second scenario is clearly correct because the fetchData
promise isn't done when [Link] finishes, so runTimer gets picked up from
the callback queue.
I'm not so sure about the first scenario... Suppose you never call
[Link](showData) then showData will never run (obviously). Now,
suppose you keep the fetchData promise object around for a while, do a
bunch of stuff (a for loop logging "hello" to the console a few thousand times),
and only then call [Link](showData) . Surely, the runTimer callback
would have had the chance to get picked up from the callback queue, after
one of the [Link]("hello") calls in the loop, when the call stack is empty,
and there's nothing on the microtask queue yet.
My version: Fetching takes 200ms time and consoling takes 250ms:
-- setTimeout communicates with Web API
-- [Link] is in Call Stack. [0ms]
-- runTimer is ready waiting in Callback Queue [0ms]
-- fetch communicates with Web aPI [1ms]
-- [Link] run and consoled and removed from Call Stack [250ms]
-- runTimer run in Call Stack [251ms]
-- fetchData promise puts showData on microtask queue [251ms]
-- showData is ready waiting in Microtask Queue [251ms]
-- showData run in Call Stack [251ms]
Maybe I should just test it and post the results here :)
2 likes Thread
İnanç Akduvan • Oct 6 '22 • Edited on Oct 6
You might be right 🙂 but probably, Call Stack will never be empty during the
for loop. Therefore runTimer cannot find a chance to jump in Call Stack.
One of the reasons that I guess like that:
for loop is inside Call Stack as a whole code block, so while passing from
one [Link] to another one, Call stack is not actually empty at any
moment.
But as you said, the best way to learn is testing. If you test it, please let us
know the results here ✌️
1 like Reply
quanghm27 • Sep 28 '22
Thank you so much, it's worth reading
4 likes Reply
İnanç Akduvan • Sep 28 '22
Thank you 🙏
1 like Reply
shammisharma • Sep 29 '22
very well explained. Thanks.
Understanding these basics save you a lot of trouble later. Been there 😅
3 likes Reply
İnanç Akduvan • Sep 29 '22
I totally agree with this 🙂 thank you!
1 like Reply
Dhruv Joshi • Sep 29 '22
probably the best explanation!
3 likes Reply
İnanç Akduvan • Sep 29 '22
thank you Joshi! 🙂
1 like Reply
fruntend • Oct 3 '22
Сongratulations 🥳! Your article hit the top posts for the week - [Link]/fruntend/top-
10-posts-for-f...
Keep it up 🫰
2 likes Reply
İnanç Akduvan • Oct 3 '22
Thank you for sharing 🤟✌️
1 like Reply
ljq0226 • Oct 5 '22
amazing!
Thank you for sharing!
2 likes Reply
Damla köker • Sep 28 '22
Amazing! Thank you :)
2 likes Reply
İnanç Akduvan • Sep 28 '22
Thanks! 🙏
1 like Reply
Zakariya Khan • Oct 5 '22
Very nice and simple explanation! I like it. ❣️
1 like Reply
İnanç Akduvan • Oct 5 '22
Glad you like it, thanks Zakariya 🙏
1 like Reply
Youssef Shaaban • Sep 17 '24
Thanks for the explanation, really clear and straight forward.
2 likes Reply
İnanç Akduvan • Sep 17 '24
Glad to hear you liked it, thanks!
1 like Reply
View full discussion (22 comments)
Code of Conduct • Report abuse
MongoDB PROMOTED
Build seamlessly, securely, and flexibly with
MongoDB Atlas. Try free.
MongoDB Atlas lets you build and run modern apps in 125+ regions across AWS,
Azure, and Google Cloud. Multi-cloud clusters distribute data seamlessly and auto-
failover between providers for high availability and flexibility. Start free!
Learn More
💎 DEV Diamond Sponsors
Thank you to our Diamond Sponsors for supporting the DEV Community
Google AI is the official AI Model and Neon is the official database partner of Algolia is the official search partner of
Platform Partner of DEV DEV DEV
DEV Community — A space to discuss and keep up software development and manage your software career
Home DEV++ Videos DEV Education Tracks DEV Challenges DEV Help Advertise on DEV Organization Accounts DEV Showcase About Contact Free Postgres Database
Forem Shop MLH
Code of Conduct Privacy Policy Terms of Use
Built on Forem — the open source software that powers DEV and other inclusive communities.
Made with love and Ruby on Rails. DEV Community © 2016 - 2026.