What are WeakMaps?
December 18, 2024
Task Queue
Instead, the successCallback
is added to the Task Queue
(also called the Callback Queue
for this exact reason). The Task Queue
holds Web API callbacks and event handlers waiting to be executed at some point in the future.
Okay so now the successCallback
is on the task queue... But when does it get executed?
Event Loop
Finally, we get to the Event Loop
! It's the responsibility of the Event Loop
to continuously check if the Call Stack
is empty.
Whenever the Call Stack
is empty — meaning there are no currently running tasks — it takes the first available task from the Task Queue
and moves this onto the Call Stack
, where the callback gets executed.
The Event Loop
continuously checks if the call stack is empty, and if that's the case, checks for the first available task in the Task Queue
, and moves this to the Call Stack
for execution.
Another popular callback-based Web API is setTimeout
. Whenever we call setTimeout
, the function call is pushed onto the Call Stack
, which is only responsible for initiating a timer with the specified delay. In the background, the browser keeps track of the timers.
Once a timer expires, the timer's callback
is enqueued on the Task Queue
! It's important to remember that the delay specifies the time after which the callback is pushed to the Task Queue
, not the Call Stack
.
This means that the actual delay to execution might be longer than the specified delay passed to setTimeout
! If the Call Stack
is still busy handling other tasks, the callback would have to wait in the Task Queue
.
So far, we've seen how callback-based APIs are handled. However, most modern Web APIs use a promise-based approach, and, as you may have guessed, these are handled differently.
I'll assume some basic knowledge about Promises from here on. My video on Promises could help if you need a refresher!
Microtask Queue
Most (modern) Web APIs
return a promise allow us to handle the returned data through chaining promise handlers (or by using await
) instead of using callbacks.
Next, the console.log
is pushed to the Call Stack
, and logs End of script
to the console. In this case, the network request is still pending.
When the server finally returns the data, the [[PromiseStatus]]
is set to "fulfilled"
, the [[PromiseResult]]
is set to the Response
object. As the promise resolves, the PromiseReaction is pushed onto the Microtask Queue
.
When the Call Stack
is empty, the Event Loop
moves the handler callback from the Microtask Queue
onto the the Call Stack
, where it's executed, logs the Response
object, and eventually popped off the call stack.
Are all Web APIs handled asynchronously?
No, just the ones that initiate asynchronous operations. Other methods, for example document.getElementById()
or localStorage.setItem()
, are handled synchronously.
Recap
Let's recap what we covered so far:
JavaScript is single-threaded, meaning it can only handle one task at a time.
Web APIs
are used to interact with features leveraged by the browser. Some of these APIs allow us to initiate async tasks in the background.The function call that initiates the async task is added to the
Call Stack
, but that is just to hand it off to the browser. The actual async task is handled in the background, and does not remain on theCall Stack
.The
Task Queue
is used by callback-basedWeb APIs
to enqueue the callbacks once the asynchronous task has completed.The
Microtask Queue
is used by Promise handlers,async
function bodies followingawait
,MutationObserver
callbacks andqueueMicrotask
callbacks. This queue has priority over theTask Queue
.When the
Call Stack
is empty, theEvent Loop
first moves tasks from theMicrotask Queue
until this queue is completely empty. Then, it moves on to theTask Queue
, where it moves the first available task to theCall Stack
. After handling the first available task, it "starts over" by again checking theMicrotask Queue
.
Promisifying callback-based APIS
To enhance readability and manage the flow of async operations in callback-based Web APIs, we can wrap them in a Promise.
For example, we can wrap the Geolocation API's callback-based getCurrentPosition
method in a Promise
constructor.