Register to get access to free programming courses with interactive exercises

Call Stack JS: Asynchronous programming

Before we dive into asynchronous programming, let's observe one key point related to code execution.

Take a look at this example:

const data = [16, 64, 4];
const data2 = data.map(Math.sqrt); // [4, 8, 2]
const predicate = (v) => v > 2;
const data3 = data2.filter(predicate); // [4, 8]

Each line in the code above runs only when the previous line has already been executed. In programming, this sequential execution of code is called synchronous. And the execution of each particular line can be as complex as you like.

If you look at the second line, you can see that we call the map() method, which internally calls Math.sqrt(). In real-world applications, the depth of nesting with functions can be enormous, reaching hundreds of levels.

During code execution, this kind of failure creates a call stack. Why a stack? Because that's how the code execution process works.

Each internal call adds the current function inside the stack. That keeps happening until we reach the deepest function possible. Then, when it comes to returning, the stack comes undone. The functions are retrieved from it one by one in reverse order and continue their execution from the moment where the internal function has returned the result:

Call-stack

Imagine we have a chain of functions one() calls two(), which calls three(), which calls four():

const four  = () => console.log('END!');
const three = () => four();
const two   = () => three();
const one   = () => two();

one(); // Running

Then the code execution will look like this:

one
=> two
   => three
      => four
   => three
=> two
one

So, first, there is a dive to the nested call, then a climb up to the first function in the call stack.

As developers, we see the call stack every day in the error output. A backtrace is nothing more than the call stack written in reverse order. To illustrate, let's make a mistake in the third line of our code:

const data = [16, 64, 4];
const data2 = data.map(Math.sqrt); // [4, 8, 2]
const predicate = (v) => unknown > 2;
const data3 = data2.filter(predicate); // ReferenceError

Let's observe its running and output. The code is in the index.js:

node index.js

index.js:3
const predicate = (v) => unknown > 2;
                               ^

ReferenceError: unknown is not defined
    at predicate (index.js:3:32)
    at Array.filter (<anonymous>)
    at Object.<anonymous> (index.js:4:21)

It is important to understand that the call stack grows only when calls go further in depth. You can see this in the output: the backtrace does not include the first and second lines, it describes the sequence starting from the filter call and beyond.

Like in other languages, the exception mechanism in JavaScript relies entirely on the existence of a call stack. Moreover, an exception mechanism makes it easier to unwind this stack. Any exception that arises moves up the call stack until it encounters a try/catch construct or until the call stack runs out.

const data = [16, 64, 4];
const data2 = data.map(Math.sqrt); // [4, 8, 2]
const predicate = (v) => unknown > 2;

try {
  const data3 = data2.filter(predicate); // ReferenceError
} catch (e) {
  console.log('Catch it');
  console.log(e.stack);
}

The declaration of the predicate() function contains an error. Even though it is outside the try/catch block, an error will still be caught inside this function since predicate() is called internally along the chain:

node index.js

Catch it
ReferenceError: unknown is not defined
    at predicate (index.js:3:32)
    at Array.filter (<anonymous>)

There are some tools for visualizing the call stack. Usually such tools help in profiling, the process of finding bottlenecks to speed up the application.

Call-stack-analyze

But everything changes when it comes to asynchronous code. More on this we will discuss later in the course.


Recommended materials

  1. Stack
  2. Call Stack

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs
profession
Development of front-end components for web applications
10 months
from scratch
Start at any time

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.