Event loop - jellyfish-tom/TIL GitHub Wiki

[SOURCES]

“How is JavaScript asynchronous and single-threaded ?”

The short answer is that JavaScript language is single-threaded and the asynchronous behaviour is not part of the JavaScript language itself, rather they are built on top of the core JavaScript language in the browser (or the programming environment) and accessed through the browser APIs.

Heap - Objects are allocated in a heap which is just a name to denote a large mostly unstructured region of memory. All the memory allocation to variables and objects happens here (inner structure of JS)

Stack - This represents the single thread provided for JavaScript code execution. Function calls form a stack of frames (inner structure of JS)

Browser or Web APIs - are built into your web browser, and are able to expose data from the browser and surrounding computer environment and do useful complex things with it. They are not part of the JavaScript language itself, rather they are built on top of the core JavaScript language, providing you with extra superpowers to use in your JavaScript code. For example, the Geolocation API provides some simple JavaScript constructs for retrieving location data so you can say, plot your location on a Google Map. In the background, the browser is actually using some complex lower-level code (e.g. C++) to communicate with the device’s GPS hardware (or whatever is available to determine position data), retrieve position data, and return it to the browser environment to use in your code. But again, this complexity is abstracted away from you by the API (not a structure of JS)

Execution order

  • when methods are called they are pushed to a stack as a frames
  • since JS is single-threaded, methods are delayed using some external (to language) APIs
  • ex. setTimeout - its execution is handed to the browser
  • browser can push anything to call stack *only when it is empty (that is why execution of all frames need to finish before setTimeout callback is pushed to it and can execute)
function main(){
  console.log('A');
  setTimeout(function display(){
    console.log('B');
  }, 0);
  console.log('C');
}
main();
//	Output
//	A
//	C
//      B

Event Table / Event Queue / Call stack / Event Loop

- Event Table - data structure that connects event with their callbacks / functions. It keeps track of events and sends them to the Event Queue

- Event Queue - it receives the function calls from the Event Table and it is queued by the Event Loop (only when Call Stack is empty)

- Call Stack - data structure which records the function calls, basically where in the program we are. If we call a function to execute , we push something on to the stack, and when we return from a function, we pop off the top of the stack.

Important pieces also called "Dictionary"

blocking statement - piece of code (statement) that executes on call stack (and hence, blocks it), preventing any other code from executing (instead of ex. being handed to browser, to execute in it, allowing other code to run)

tick - one run of event loop (one pool to call stack)

web workers - they enable you to offload an expensive operation to a separate thread of execution, freeing up the main thread to do other things. Worker includes a separate message queue, event loop, and memory space independent from the original thread that instantiated it. Communication between the worker and the main thread is done via message passing, which looks very much like the traditional, evented code-examples we’ve already seen.

Tricks

Want to hack stackOverflow error in JS that occured because of too many callbacks being runned? Wrap each callback in setTimeout. Each call will go through the event table and queue instead of directly piling up on the stack.

Max call stack size is 16000 frames

Microtask / Macrotask queue

console.log('111')

setTimeout(() => {
  console.log('333')
}, 0)

Promise.resolve().then(() => {
  console.log('444')
})

console.log('222')

There are actually two different task queues in JavaScript, namely microtask queue and macrotask queue.

When the execution stack is idle, JavaScript will first execute the tasks in the micro task queue, and will not execute the tasks in the macro task queue until all the tasks in the micro task queue have been executed. In other words, the micro task queue is like a first-class citizen with a better priority; the macro task queue is like a second-class tool with a lower priority.

Here are lists of macrotasks and microtasks.

MICRO:

  • Promise
  • async
  • MutationObserver
  • queueMicrotask
  • process.nextTick

In Node.js, each iteration of an event loop is called a tick. The callback passed to the process.nextTick() will be executed in the current phase of the event loop. process.nextTick() is not part of event loop though its part of asynchronous API.

MACRO:

  • setTimeout()
  • setImmediate()
  • setInterval()
  • requestAnimationFrame()
  • I/O
  • UI rendering

Finally, let’s talk about asnyc/await. How to understand this feature? In fact, async/await is syntactic sugar for Promise. As such it will behave the same as Promise.