Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

In the last module of the JavaScript Asynchronous Programming Guide, I focused on the basic applications of asynchronous programming. In this module series, I want to talk about event loops, called Eventloops.

This name will be familiar to those of you who have participated in JavaScript interviews (including front-end or back-end Node.js).

There are a lot of articles on event loops, but not many in a series. I put event loops in the second module of the JavaScript Asynchronous Programming Guide series, hoping to gain a deeper understanding of JavaScript asynchronous programming.

Learn pre-event loops

JavaScript is a programming language that can be run on either the client browser or node.js server. I want to do it in a way that I understand, so I’m not going to go directly to EventLoop in the browser or EventLoop in Node.js.

Some of the concepts in event loops are universal when we learn about event loops in browsers or Node.js, and will be easier to understand later.

The words single-thread, call stack, heap, queue, Eventloop look like this visually, but how do they relate to each other? Next, I will introduce them separately.

Why single thread?

JavaScript is single threaded, and at this point, is there any question why single threaded? Isn’t multithreading more efficient?

Let’s start with the browser. For DOM manipulation in the browser environment, imagine if multiple threads are working on the same DOM. One thread adds the DOM and another thread removes it. Is it a mess? That means DOM operations can only be single-threaded, avoiding DOM rendering conflicts.

The UI rendering thread and the JavaScript execution engine are mutually exclusive in the browser environment, and each execution causes the other to be suspended.

Since JavaScript is single-threaded, only one thing can be processed at a time.

The answer is No. The solution to blocking waiting is asynchrony. For example, when a program initiates a network request or file request, it does not have to wait for the response result synchronously. In JavaScript, it is realized by single thread plus event loop, which also avoids multi-thread context switch and resource preemption, and achieves better high concurrency achievement.

Additionally, HTML5 introduces the Web Worker standard, and Node.js provides the worker_Threads module, which allows us to create multiple threads in the service, but none of this changes the nature of JavaScript single threads, whether they are child threads or managed by the main thread.

The call stack

The stack is an in-and-out data structure, and JavaScript is a single-threaded programming language that can only run one piece of code at a time, with one and only one call stack.

All tasks in JavaScript fall into two categories: synchronous and asynchronous.

In the first case, synchronization tasks are queued on the main thread, forming a Call Stack consisting of several frames.

In the following example, when the hello() function is called, the first frame is created and pushed onto the stack, which in turn calls the intro() function, and the second frame is created and pushed on top of hello(). The intro() function will execute until the second frame is ejected from the stack, and then the hello() function will execute. When the first frame is ejected from the stack, the stack will be emptied.

function intro() {
	console.log('My name is codingMay! ');
}
function hello() {
  intro();
	console.log('Hello');
}
hello();
Copy the code

Show the running results through the way of dynamic diagram.

In development, it is also inevitable that in some cases the program will throw error messages, whether it is a displayed error definition or an unexpected unknown error.

Let’s modify the example by having intro() throw an Error object, which prints out the entire stack of Error calls from intro to Hello to anonymous functions after the Chrome console runs. This is a synchronous call where the context information is related and the program is able to track down some code to be executed on the next line.You may also have heard of a problem called “memory leak”. Here’s an example on the left. The hello() function recursively calls itself. The program keeps going like this, calling the stack and adding data until the stack’s maximum space limit is exceeded, and the program will report an errorVM356:4 Uncaught RangeError: Maximum call stack size exceeded.Consider the question “How can the recursive code above be modified so that it does not trigger stack overflow? If it’s still recursive.”

The heap

When JavaScript is executed, all data will be stored in memory, such as functions, function variables, parameters and other known data occupying space exist in the stack of memory area, and the objects created during code execution exist in the heap, which is another area of memory.

Queues and callback functions

In JavaScript when something still execute the call stack, our program is not free to perform other operations, just think, if the call stack some very time consuming task, if it is used in the client user will see a page is stuck, if it is on the server may result in the interface response is slow, there would be no concurrent advantages, This is a bad thing, we can’t let the JavaScript main thread block.

Modify the above example by adding a setTimeout delay to the inrto() method and see how the program performs.

function intro() {
	setTimeout(function timer() {
  	console.log('My name is codingMay! ');
  }, 8000)}function hello() {
  intro();
	console.log('Hello');
}
hello(); 
Copy the code

In the above code, the setTimeout timer function is executed inside the intro() function. This is asynchronous, and our JavaScript main thread will not wait here, but will return immediately. So setTimeout, the first parameter that we pass in is the timer and this is the code that we need to execute, where timer is usually what we call a callback function.

Note: Web Apis this is an API provided by the host environment. There are also separate threads for implementation, such as timers implemented by the host environment.SetTimeout is not implemented by the JavaScript engine. It is provided by the host environment in which the JavaScript program runs, and it is not difficult to understand this concept. On the client side, our host environment is the browser, and on the server side, it is Node.js.When the timer runs out, the host environment encapsulates the timer function as an event into a “queue,” which is a first-in, first-out data structure.

The next task to execute in the queue is EventLoop

EventLoop

EventLoop is a continuous Loop that checks if the current stack is empty and only enters the next Loop if the stack is empty. If there is a task in the task queue, it executes it. If the task queue is empty, it waits synchronously for the message to arrive.

Implement it in a similar way as follows:

while (queue.waitForMessage()) {
  queue.processNextMessage(); // Wait synchronously for messages to arrive
}
Copy the code

A Gif showing the full effect, slightly grainy because it was compressed during upload.

Reference

  • latentflip.com/loupe
  • Developer.mozilla.org/zh-CN/docs/…
  • medium.com/@sanderdebr…
  • Developer.mozilla.org/zh-CN/docs/…