This article is based on JavaScript Event Loop And Call Stack Explained. Written with additional information. If you want to learn directly from the text, you can ignore this article. But don’t forget to like + follow.

If you feel ok, please like more, encourage me to write more wonderful article 🙏.

If you have any questions, feel free to comment in the comments section


The profile

  • 1.How does JS run in a browser
    • The call stack
      • Stack overflow
    • Web APIs
    • The callback queue
    • Event loop
      • setTimeout(fn,0)
    • Work queues and asynchronous code
      • Promises
      • Promises are good for Where

How does JS run in a browser

To understand the original features of a thing, we need a strategically positioned way to grasp the context and development of the whole thing as a whole.

And the following picture shows how JS is inThe browserThe trajectory of the run in.

One thing to note: most of the data structures and components involved in the image are not provided by JS. Web APIs, callback queues, event loops are all browser-sponsored.

The call stack

When we first encounter JS, whether from textbooks or other supporting materials, the first concept that catches our eye is that JS is a single-threaded language. What are the conditions under which a single thread can say that?

JS can only do one thing at a time, because JS has one and only one call stack.

The call stack is a mechanism that helps the JS compiler track the order in which functions are called.

When a script or function calls another function, the called function is added to the top of the call stack. (Borrowing from the knowledge of data structures, that is, data pushing).

When a function exits (either by returning or by going to the bottom of the function), the editor removes the function from the top of the call stack.

Let’s trace how a function call works with the call stack through a simple example.

const addOne = value => value + 1; const addTwo = value => addOne(value + 1); const addThree = value => addTwo(value + 1); const calculation = () => addThree(1) + addTwo(2); // function call calculation();Copy the code

Whenever A function calls (A) another function (B), the called function is pushed to the top of the call stack (above the function (A) that was just called).

The order in which the call stack processes the called functions follows LIFO (Last in,First Out) principles.

In the example above, follow the following steps:

  1. The file is loaded andmianFunction (Represents the entire fileIs called, and the function is calledpushTo the call stack. (In this case, the function is both the bottom and the top of the stack)
  2. mianA function callcalculation(), similarly, the function is also treated bypushTo the top.
  3. calculation()A function calladdThree(), similarly, the function is also treated bypushTo the top.

. Repeat until addOne() is called

  1. addOne()No function is called. So whenaddOne() exit(to performreturnOr to the bottom of the function), which will be called from the top of the stackpopAnd out the other.
  2. inaddtTwo()In the processingaddtOne()The return result is also experienced withaddtOne()The same fateexit(to performreturnOr to the bottom of the function), which will be called from the top of the stackpopAnd out the other.

. Repeat until calculation() is called

  1. No statement or function is called in the file. somain()performexitOperation.

We referred to the function execution environment as
main()In the browser, it is an anonymous function –
anonymous


Stack overflow

In our peacetime, the development of code, always a careless, will produce a dead cycle, and the cause of the dead cycle is very simple, is a function or code, always keep iteration cycle – endless, there is a determination and perseverance to the Yellow River.

As we know from the operation mechanism of the call stack introduced above, when there is a function call, the called function will be pushed to the top of the call stack. And we know that no matter what language, there is a MAX storage space for a given piece of data.

So, conceivably, a stack overflow occurs when called functions are pushed endlessly onto the call stack, and at some point, more functions are added than the stack can tolerate.

So, the browser will give you a hint:

Uncaught RangeError: Maximum call stack size exceeded
Copy the code

We know from the error message. It means that the function call caused the error. In this case, the error occurs in function B, which is called by function A.

If you encounter this error during development, you will find that a particular function is called many times through the display of error messages. The maximum range of call stacks is 10,000 to 50,000 times. If your function is called that many times, it means you have an infinite loop in your code.

Browsers limit the storage size of the call stack to prevent you from inadvertently writing endless loops that cause pages to hang.

conclusion: call stack used for
trackingThe order in which functions are called in your code. It follows
LIFOThe (last in, first out) principle means that it always deals with functions that are placed at the top of the stack.

JS has one and only one call stack –> means JS can only do one thing at a time.


Web APIs

From the concepts we learned above, JS can only do one thing at a time.

However, there is a big cavity-that this rule only applies to JS. Once this condition is extended to the browser, it will have a different effect. Browser-wide, while JS is running, other things can be done in parallel via browser-provided apis.

Browsers provide us with apis that we can call in our JS code. These apis are then executed by the platform (browser, Node…) This is why these apis do not block the execution of the call stack.

Another advantage is that these apis are written by the underlying language (C) and can do things that JS can’t.

These apis enable your JS code to make AJAX requests, manipulate DOM, access local storage, use workers, and more.


The callback queue

By using the API that the browser provides for us, we can easily implement -> do extra things while JS is running. But how do we get and use the results returned by the Web API in the JS code we maintain?

This is where the callback comes in. With callback, the Web API allows us to trigger the specified callback function after the API calls and returns the corresponding result.

What is callback?


A callback is a
As a parameterPassed to another function
function.


Callback is executed only after the calling function has been executed.



Higher-order function: a function that takes a function as an argument

const a = () = > console.log('a');
const b = () = > setTimeout(() = > console.log('b'), 100);
const c = () = > console.log('c');

a();
b();
c();
Copy the code

Because setTimeout is a Web API. Therefore, when setTimeout is executed, the execution flow of web API is triggered. The JS interpreter continues to execute the rest of the statement.

When timeout has arrived and the call stack is empty, the callback passed to setTimeout will be executed.

The following output is displayed:

a
c
b
Copy the code

What is a callback queue

Although setTimeout is executed, its callback function is not called immediately.

Remember how JS can only do one thing at a time?

We pass a function as an argument to setTimeout, whose callback function for setTimeout is written in JS. Therefore, the JS interpreter needs to run this code, which means that this code needs to be pushed onto the call stack. The condition for being pushed is that the call stack is empty. So, we need to wait.

Call setTimeout, which triggers the execution of the Web API, and enqueue the callback. When the call stack is empty, the event loop dequeues the newly enqueued callback from the callback queue and pushes it to the call stack.

Unlike the call stack, the callback queue follows a FIFO(First In, First Out), meaning that callbacks are processed In the order In which they were queued.


Event loop

The JS event loop always fetches the element at the head of the callback queue and pushes it to the Call stack when the Call stack is empty.

The JS code follows a consistent running pattern, that is, if there is code in the Call stack, the event loop will block until the Call stack is empty, Callback QueuedeQueue pushes to the call stack.

Do not clog the call stack by running computationally intensive tasks. If you execute too much time-consuming code in the code you maintain, the event loop stays blocked and the site gets stuck.

setTimeout

If we want to perform certain tasks without blocking the main thread for too long, we can use the behavior described above.

Place asynchronous code in the callback and set setTimeout to 0ms, which allows the browser to perform operations such as updating the DOM before continuing with the callback.


Work queues and asynchronous code

In addition to the callback queue, there is another queue in the browser that is specifically designed to receive Promises: Job Queue.

Promises

ES6 introduces Promises for the first time. Promises can be recognized and used by major browsers through Babel conversion.

Promises is another way to handle asynchronous code differently than using callback. They allow you to easily chain asynchronous functions together without ending up in so-called callback hell.

setTimeout(() = > {
  console.log('Print this and wait');
  setTimeout(() = > {
    console.log('Do something else and wait');
    setTimeout(() = > {
      // ...
    }, 100);
  }, 100);
}, 100)
Copy the code

If you have a lot of asynchronous dependencies, you’re in callback hell.

With Promises, the above code is much more readable.

const timeout = (time) = > new Promise(resolve= > setTimeout(resolve, time));
timeout(1000)
  .then(() = > {
    console.log('Hi after 1 second');
    return timeout(1000);
  })
  .then(() = > {
    console.log('Hi after 2 seconds');
  });
Copy the code

It’s even more concise if you use ES7’s async/await syntax.

const logDelayedMessages = async () => {
  await timeout(1000);
  console.log('Hi after 1 second');
  await timeout(1000);
  console.log('Hi after 2 seconds');
};

logDelayedMessages();
Copy the code

Promises are good for Where

Promises behave a little differently than callbacks because they have their own queue.

The work queue, also known as the Promise queue, has a higher priority than the callback queue.

This means that the Event loop will traverse the Promise queue first and the callback queue only when the Promise Queue is empty.

console.log('a');
setTimeout(() => console.log('b'), 0);
new Promise((resolve, reject) => {
  resolve();
})
.then(() => {
  console.log('c');
});
console.log('d');
Copy the code

The corresponding output is

a
d
c
b
Copy the code