What is the JavaScript runtime?

Nicholas Mendez - May 29 '21 - - Dev Community

Why Should I care?

A basic understanding of asynchronous programming is needed to build dynamic AJAX web apps. Most frontend developers perform asynchronous programming when working with JavaScript and it is due to the nature of the JavaScript runtime.

What's a Runtime?

A runtime is the environment in which a programming language executes. The runtime system facilitates storing functions, variables, and managing memory by using data structures such as queues, heaps and stacks (more on this later).

Before you proceed you should know about the following 3 computing concepts:

The Call Stack

When you write a program, you may compose it of multiple functions. The call stack keeps track all function calls during throughout the life time of the program and execute them in the reverse order that they are called.

Hence why crashing a program with never ending recursive function calls is said to be a stack/buffer overflow. The stack had so many function calls that it ran out of memory space.

ezgif-6-6ad01ce45325

Threads
In an OS you can run an program which can be comprised of processes. A process can then be comprised of multiple threads. A thread is the smallest unit of computation that can be individually scheduled.

Multithreading
Computers with multiple cores can process multiple threads at the same time. Some programming languages support multithreading by allowing your program to spawn child threads to perform a task then return the result to the parent. Such runtime would provide multiple call stacks. Each call stack is delegated to a thread.

What makes JavaScript's runtime so special?

By design the JavaScript interpreter is single-threaded, this is a good thing because it makes it easier to implement browsers for all kinds of devices, consoles, watches fridges etc.

But then you may wonder how do web apps work if the interpreter can only do one thing at a time? Well even though JavaScript is single threaded, it executes tasks in a concurrent fashion.

Simply put, concurrency is breaking up tasks and switching between so quickly that they all appear to progress at the same time. Contrast this with parallelism which is performing tasks simultaneously.

yza7n2jl2oscv8gtiu9v

This means that there must be some scheduling mechanism to determine which task's turn is next. That leads us to the next section.

The Queues & The Event Loop

All functions must eventually reach the call stack to be executed. However, depending on where a function is called will affect its priority in reaching the call stack.

Function Call Timing Example
Normal function call Straight to the call stack fun()
In a task Goes to task queue then onto the call stack Web APIs such as setTimeout(fun, 1000);
In a microtask Goes to the micortask queue then onto the call stack After a promise resolves eg fetch().then(fun);

Tasks and microtasks are operations in JavaScript which must go on a queue before reaching the call stack.

The event loop is a mechanism in the runtime that moves tasks and microtask from their respective queue onto the call stack.

ezgif.com-gif-maker

The event loop will execute 1 task at until the browser renders the next frame of pixels onto the display. However with microtasks, all will be executed before the next render.

...OK but what does this all mean?

Simply put, certain operations in JavaScript are executed with different priority levels. Therefore these operations may finish their execution in a different order than they were called. This is what happens in Asynchronous Programming and it can throw off programmers new to the concept.

fetch() is an example of an async call. Appreciating that it is asynchronous will help you use it properly. For example a common pitfall is the following.

let myreponse = 'loading...';

fetch('https://data.org/users')
   .then(function(response){ 
      myresponse = response; //executes 2nd
    });

handleResponse(myresponse); //executes 1st, does not get the response
Enter fullscreen mode Exit fullscreen mode

Because fetch is an async call, the function which assigns myresponse to response will go on the task queue and will execute AFTER handleResponse(). Hence handleResponse() will be called with the value 'loading...' instead of the actual response.

If you need to do something with the output of a async call, it should be done within the scope of the task.

//avoid using global variables with async

fetch('https://data.org/users')
   .then(function(response){
      handleResponse(response); //gets the response
   });
Enter fullscreen mode Exit fullscreen mode

This can be shortened further because handleResponse() takes only one parameter.

fetch('https://data.org/users').then(handleResponse);
Enter fullscreen mode Exit fullscreen mode

Conclusion

And that's what the JavaScript Runtime is about! If things got too heavy that's ok. Just keep in mind that some functions in JavaScript are async and may not run in the order you expect. When that happens you should read up on it to know how to use it properly.

The animations in the post was created with this awesome tool called JS Visualizer 9000.

Here are some great talks that also explain this concept more visually:

  1. Philip Roberts - What the heck is the event loop anyway
  2. Jake Archibald - In the Loop

References

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player