Data Structures Used In Browsers

Data Structures Used In Browsers

What drives async programming in JavaScript?

·

4 min read

As we all know, JavaScript is a single-threaded language. If there is simple synchronous code then code execution happens one after the other and it's all a piece of cake. In reality however, we often find ourselves dealing with asynchronous events and we want JavaScript to handle these events in a non-blocking manner so that our code is not stuck while waiting for an async event to complete. Therefore, to provide asynchronous capabilities, the following data structures are used within browsers to make it work seamlessly.

The Call Stack

The call stack is used to manage the execution contexts for your JS code. It is handled by the JS engine, which creates the contexts, pushes it into the stack, executes code and pops the contexts out.

There are basically two types of execution contexts:

  • Global Execution Context: When your JS code starts executing, the JS engine creates a GEC and pushes it into the call stack. There is only one GEC created which then executes your JS code from top to bottom, line by line. After reaching the end of your code, this GEC is popped out from the stack.

  • Function Execution Context: When the JS engine comes across a function call in your code, it creates a new execution context for it and pushes it into the call stack. There can be many function execution contexts in the call stack as a new one is created for every function.

Any code that has to be executed has to be put into the call stack first.

The Task Queue or Callback Queue

When the JS engine comes across some asynchronous code that is blocking in nature (could be some Web APIs like setTimeout or an event listener), a callback function is registered in the Web APIs environment. This callback function cannot be put into the call stack directly so the Web APIs push them into an alternate area where it can wait for the call stack to be ready. This alternate area is called the callback queue or the task queue.

💡
Web APIs is a bunch of built-in utility functions provided by the browser that can be used by your JS code. These functions are not part of JS but any code written in JS can use these. These are: Timeout API, Fetch API, DOM API, Console API, Local Storage API, Location API.

Therefore, once the timeout has elapsed or the event has occurred, the callback function is pushed into the callback queue by the Web APIs. Once the call stack becomes empty and available, the callback from this callback queue is pushed into the call stack by the Event Loop.

💡
The Event Loop is an infinite loop that keeps running in the browser and continuously checks the callback queue and the call stack. When it finds that the the Global Execution Context has been removed from the call stack and there are functions waiting in the callback queue, it pushes these functions one by one into the call stack.

The Microtask Queue

Some Web APIs return a promise upon completion of an async computation for example: fetch() call which makes a network request to a third-party and returns a promise. The callbacks coming from such Web APIs are again registered in it environment but they are not pushed into the callback queue. These callbacks are pushed into a separate queue called the microtask queue. The microtask queue takes higher priority than the callback queue, meaning all the callbacks in the microtask queue will be executed first then only the next callback from callback queue will start executing. (by executing, I mean being pushed into the call stack).

The microtask queue therefore is responsible for holding high-priority callbacks that need to be immediately executed as soon as the call stack becomes ready.

These three data structures combined together with Event Loop and Web APIs enables JavaScript to handle asynchronous programming albeit being a single-threaded language. Truly wonderful, isn't it?

Well, thanks for reading and I hope it helped you. See you in the next one! ✨