Home  >  Article  >  Web Front-end  >  Is javascript a multi-threaded language?

Is javascript a multi-threaded language?

青灯夜游
青灯夜游Original
2022-02-18 17:55:093585browse

Javascript is not a multi-threaded language, but a single-threaded language. JavaScript is a browser scripting language, and its interpreter is single-threaded; and the main purpose of JavaScript is to interact with users and operate the DOM, which determines that it can only be single-threaded, otherwise it will cause very complex synchronization problems.

Is javascript a multi-threaded language?

The operating environment of this tutorial: windows7 system, javascript version 1.8.5, Dell G3 computer.

Javascript is not a multi-threaded language, but a single-threaded language. The JavaScript language also does not support multi-threading, because the JavaScript interpreter in the browser is single-threaded.

A major feature of the JavaScript language is single-threading, which means that it can only do one thing at the same time.

So, why can’t JavaScript have multiple threads? This can improve efficiency.

The single thread of JavaScript is related to its purpose. As a browser scripting language, JavaScript's main purpose is to interact with users and manipulate the DOM. This determines that it can only be single-threaded, otherwise it will cause very complex synchronization problems.

In order to take advantage of the computing power of multi-core CPUs, HTML5 proposes the Web Worker standard, which allows JavaScript scripts to create multiple threads, but the child threads are completely controlled by the main thread and must not operate the DOM. Therefore, this new standard does not change the single-threaded nature of JavaScript.

Task Queue

Single thread means that all tasks need to be queued, and the next task will not be executed until the previous task is completed. If the previous task takes a long time, the next task will have to wait.

If the queue is due to a large amount of calculation and the CPU is too busy, forget it, but many times the CPU is idle because the IO device (input and output device) is very slow (for example, Ajax operations read from the network (get data), you have to wait for the results to come out before proceeding.

The designers of the JavaScript language realized that at this time, the main thread can completely ignore the IO device, suspend the waiting tasks, and run the later tasks first. Wait until the IO device returns the result, then go back and continue executing the suspended task.

So, all tasks can be divided into two types, one is synchronous task (synchronous), the other is asynchronous task (asynchronous).

Synchronous tasks refer to tasks queued for execution on the main thread. The next task can only be executed after the previous task has been executed;

Asynchronous tasks refer to tasks that do not enter the main thread. . For tasks that enter the "task queue", only when the "task queue" notifies the main thread that an asynchronous task can be executed will the task enter the main thread for execution.

Specifically, the operating mechanism of asynchronous execution is as follows. (The same is true for synchronous execution, because it can be regarded as asynchronous execution without asynchronous tasks.)

(1) All synchronous tasks are executed on the main thread, forming an execution context stack.

(2) In addition to the main thread, there is also a "task queue". As long as the asynchronous task has running results, an event is placed in the "task queue".

(3) Once all synchronization tasks in the "execution stack" are completed, the system will read the "task queue" to see what events are in it. Those corresponding asynchronous tasks end the waiting state, enter the execution stack, and start execution.

(4) The main thread continues to repeat the third step above.

The following figure is a schematic diagram of the main thread and task queue.

Is javascript a multi-threaded language?

#As long as the main thread is empty, it will read the "task queue". This is the running mechanism of JavaScript. This process keeps repeating.

Events and callback functions

"Task queue" is a queue of events (can also be understood as a queue of messages). When the IO device completes a task, it Adding an event to the "Task Queue" indicates that the related asynchronous tasks can enter the "Execution Stack". The main thread reads the "task queue", which means reading the events in it.

The events in the "Task Queue", in addition to IO device events, also include some user-generated events (such as mouse clicks, page scrolling, etc.). As long as the callback function is specified, these events will enter the "task queue" when they occur, waiting for the main thread to read.

The so-called "callback function" (callback) is the code that will be hung up by the main thread. Asynchronous tasks must specify a callback function. When the main thread starts executing an asynchronous task, the corresponding callback function is executed.

"Task queue" is a first-in, first-out data structure. The events ranked first are read by the main thread first. The reading process of the main thread is basically automatic. As soon as the execution stack is cleared, the first event on the "task queue" will automatically enter the main thread. However, due to the "timer" function mentioned later, the main thread must first check the execution time. Certain events can only return to the main thread after the specified time.

Event Loop

The main thread reads events from the "task queue". This process is cyclic, so the entire running mechanism is also called Event Loop.

To better understand Event Loop, please look at the picture below.

Is javascript a multi-threaded language?

In the above figure, when the main thread is running, a heap and a stack are generated. The code in the stack calls various external APIs. They are in the "task" Add various events (click, load, done) to the queue". As long as the code in the stack is executed, the main thread will read the "task queue" and execute the callback functions corresponding to those events in sequence.

The code in the execution stack (synchronous task) is always executed before reading the "task queue" (asynchronous task). Take a look at the example below.

    var req = new XMLHttpRequest();
    req.open('GET', url);    
    req.onload = function (){};    
    req.onerror = function (){};    
    req.send();

The req.send method in the above code is an Ajax operation to send data to the server. It is an asynchronous task, which means that the system will read the "task queue" only after all the codes of the current script are executed. . Therefore, it is equivalent to the following writing.

 var req = new XMLHttpRequest();
    req.open('GET', url);
    req.send();
    req.onload = function (){};    
    req.onerror = function (){};

In other words, it does not matter whether the part that specifies the callback function (onload and onerror) is before or after the send() method, because they are part of the execution stack, and the system always executes them before Will read the "task queue".

Timer

In addition to placing events for asynchronous tasks, the "Task Queue" can also place timed events, that is, specifying how long certain code will be executed after. This is called the "timer" function, which is code that is executed regularly.

The timer function is mainly completed by the two functions setTimeout() and setInterval(). Their internal operating mechanisms are exactly the same. The difference is that the code specified by the former

is executed once , the latter is executed repeatedly. The following mainly discusses setTimeout().

setTimeout() accepts two parameters, the first is the callback function, and the second is the number of milliseconds to delay execution.

console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);

The execution results of the above code are 1, 3, 2, because setTimeout() delays the execution of the second line until 1000 milliseconds later.

If the second parameter of setTimeout() is set to 0, it means that after the current code is executed (the execution stack is cleared), the specified callback function will be executed immediately (0 millisecond interval)

 setTimeout(function(){console.log(1);}, 0);
console.log(2);

The execution result of the above code is always 2, 1, because only after the second line is executed, the system will execute the callback function in the "task queue".

In short, the meaning of setTimeout(fn,0) is to specify a task to be executed in the earliest available idle time of the main thread, that is, to be executed as early as possible. It adds an event at the end of the "task queue", so it will not be executed until the synchronization task and the existing events in the "task queue" have been processed.

The HTML5 standard stipulates that the minimum value (shortest interval) of the second parameter of setTimeout() shall not be less than 4 milliseconds. If it is lower than this value, it will automatically increase. Prior to this, older browsers set the minimum interval to 10 milliseconds. In addition, those DOM changes (especially those involving page re-rendering) are usually not executed immediately, but every 16 milliseconds. At this time, the effect of using requestAnimationFrame() is better than setTimeout().

It should be noted that setTimeout() only inserts the event into the "task queue". The main thread must wait until the current code (execution stack) is finished executing before the main thread executes the callback function it specifies. If the current code takes a long time, it may take a long time, so there is no way to guarantee that the callback function will be executed at the time specified by setTimeout().

Event Loop of Node.js

Node.js is also a single-threaded Event Loop, but its operating mechanism is different from the browser environment.

Please see the diagram below

Is javascript a multi-threaded language?

According to the above diagram, the operating mechanism of Node.js is as follows.

(1) V8 engine parses JavaScript scripts.

(2) The parsed code calls the Node API.

(3) The libuv library is responsible for the execution of Node API. It allocates different tasks to different threads to form an Event Loop (event loop), and returns the execution results of the tasks to the V8 engine in an asynchronous manner.

(4) The V8 engine returns the results to the user.

In addition to the two methods setTimeout and setInterval, Node.js also provides two other methods related to "task queue": process.nextTick and setImmediate. They can help us deepen our understanding of "task queue".

The process.nextTick method can trigger the callback function at the end of the current "execution stack"—before the next Event Loop (the main thread reads the "task queue"). That is, the task it specifies always occurs before all asynchronous tasks. The setImmediate method adds an event to the end of the current "task queue", that is to say, the task it specifies is always executed in the next Event Loop, which is very similar to setTimeout(fn, 0). See the example below (via StackOverflow).

 process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});
 
setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

上面代码中,由于process.nextTick方法指定的回调函数,总是在当前”执行栈”的尾部触发,所以不仅函数A比setTimeout指定的回调函数timeout先执行,而且函数B也比timeout先执行。这说明,如果有多个process.nextTick语句(不管它们是否嵌套),将全部在当前”执行栈”执行。

现在,再看setImmediate。

 setImmediate(function A() {
  console.log(1);
  setImmediate(function B(){console.log(2);});
});
 
setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0);

上面代码中,setImmediate与setTimeout(fn,0)各自添加了一个回调函数A和timeout,都是在下一次Event Loop触发。那么,哪个回调函数先执行呢?答案是不确定。运行结果可能是1–TIMEOUT FIRED–2,也可能是TIMEOUT FIRED–1–2。

令人困惑的是,Node.js文档中称,setImmediate指定的回调函数,总是排在setTimeout前面。实际上,这种情况只发生在递归调用的时候。

 setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });
 
  setTimeout(function timeout() {
    console.log('TIMEOUT FIRED');
  }, 0);
});
// 1
// TIMEOUT FIRED
// 2

上面代码中,setImmediate和setTimeout被封装在一个setImmediate里面,它的运行结果总是1–TIMEOUT FIRED–2,这时函数A一定在timeout前面触发。至于2排在TIMEOUT FIRED的后面(即函数B在timeout后面触发),是因为setImmediate总是将事件注册到下一轮Event Loop,所以函数A和timeout是在同一轮Loop执行,而函数B在下一轮Loop执行。

我们由此得到了process.nextTick和setImmediate的一个重要区别:多个process.nextTick语句总是在当前”执行栈”一次执行完,多个setImmediate可能则需要多次loop才能执行完。事实上,这正是Node.js 10.0版添加setImmediate方法的原因,否则像下面这样的递归调用process.nextTick,将会没完没了,主线程根本不会去读取”事件队列”!

 process.nextTick(function foo() {
  process.nextTick(foo);
});

事实上,现在要是你写出递归的process.nextTick,Node.js会抛出一个警告,要求你改成setImmediate。

另外,由于process.nextTick指定的回调函数是在本次”事件循环”触发,而setImmediate指定的是在下次”事件循环”触发,所以很显然,前者总是比后者发生得早,而且执行效率也高(因为不用检查”任务队列”)。

【相关推荐:javascript学习教程

The above is the detailed content of Is javascript a multi-threaded language?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn