event loop, processes and threads, task queue

This article was originally Links: https://cloud.tencent.com/developer/article/1106531

https://cloud.tencent.com/developer/article/1372207

Look at the piece of code:

console.log(1); setTimeout(function () { console.log(2); new Promise(function (resolve, reject) { console.log(3); resolve(); console.log(4); }).then(function () { console.log(5); }); }); function fn() { console.log(6); setTimeout(function () { console.log(7); }, 50); } new Promise(function (resolve, reject) { console.log(8); Resolve ( ) ; Console . log ( . 9 ) ; } ) . the then ( function ( ) {Console . log ( 10 ) ; } ) ; Fn ( ) ; Console . log ( . 11 ) ; // The following code needs node environment performing Process . nextTick ( function ( ) {Console . log ( 12 is ) ; }); setImmediate(function () { console.log(13); });

Think about, can give an accurate output order it?

Let us one by one to understand the Event Loop relevant knowledge, and finally step by step analysis of the code is the final output of this paragraph order.

JavaScript is single-threaded

First, let's understand the concepts and relationships under process and thread:

  • Process:  program running is a process, for example, you are running a browser, it will be a process.
  • Thread:  programs run independently of code. A process  by a single or multiple  threads  composed of threads are responsible for the implementation of the code.

We all know that JavaScript is single-threaded, single-threaded since then have multiple threads, the first look at the difference between single-threaded and multi-threaded:

  • Threaded:  from the beginning to the end of execution, execution line by line, wherein the line of code if error, then the rest of the code will not be executed. At the same time the code is easy to clog.
  • Multithreading:  different code running environment, each thread is independent of each other, to avoid blocking.

Why JavaScript is single-threaded it?

JavaScript is single-threaded, and its related purposes. As a browser scripting language, JavaScript main purpose is to interact with the user, as well as operating DOM. This determines that it can only be a single-threaded, otherwise it will bring a very complex synchronization problems. For example, suppose JavaScript while there are two threads, one thread to add content on a DOM node, another thread to delete this node, then the browser should prevail which thread it?

So, to avoid complexity, from the birth, JavaScript is single-threaded, which has become a central feature of this language, will not change.

To take advantage of the computing power of multi-core CPU, HTML5 Web Worker proposed standard that allows JavaScript scripts to create multiple threads, but the main thread child thread completely under control, and shall not operate DOM. So, the new standard does not change the nature of single-threaded JavaScript.

The execution stack, the task queue

When the figure above, the main thread running, producing heap (heap) and stack (stack), the stack of code calls various external API, they are added to various events (DOM Event, ajax, setTimeout in "Task Queue" in the ... ). As long as the code is executed in the stack is completed, it will be the main thread to read the task queue, followed by the implementation of those events corresponding to the callback function.

Heap (heap):

Object is allocated in a stack, i.e. to represent a majority of unstructured memory area.

The execution stack (stack):

Run synchronization code. Stack code (sync task), always read "Task Queue" (asynchronous task) performed before.

Task Queue (callback queue):

"Task Queue" is an event queue (queue can also be understood as a message), IO devices to complete a task, you add an event in "Task Queue" in it indicates that the associated asynchronous task may enter the "execution stack" the. The main thread reads "task queue", which is to read the events there.

"Task Queue" in the event, in addition to the events IO devices, also includes a number of user-generated events (such as mouse clicks, page scrolling, etc.). Just specify the callback function too, will enter the "Task Queue" When these events occur, wait for the main thread to read.

The so-called "callback" (callback), that is, those codes will be hung up in the main thread. Asynchronous tasks must specify a callback function, when the main thread begins execution asynchronous task is to perform the corresponding callback function.

"Task Queue" is a FIFO data structure, standing in the front of the event, the priority is read the main thread. The main thread of the reading process is essentially automatic, as long as the implementation of a stack of empty, "Task Queue" one of the first events will automatically enter the main thread. However, due to the existence of "Timer" function mentioned later, the main thread should first check the execution time, certain events only to specified time, to return to the main thread.

Synchronization task, asynchronous tasks, tasks macro, micro task

Single-threaded means, all tasks need to line up, before the end of a mission, will perform a post-task. If a task takes a long time ago, a task after it had been waiting for.

If the queue is because computationally intensive, CPU busy, they do well, but many times the CPU is idle, because the IO device (input and output devices) is very slow (such as Ajax operation reads data from the network), had waiting for the results come out, and then execute down.

Designers realized JavaScript language, then the main thread can regardless of IO devices, hangs in the waiting task, run the task at the back. IO device returns to wait until the results come back, the pending tasks to continue execution.

Thus, a broad sense JavaScript all tasks can be divided into two, one is a synchronization task (Synchronous), the other is asynchronous task (asynchronous). Synchronization task means that queued for execution on the main thread task, only the first task is finished to a task after execution; asynchronous tasks mean, do not enter the main thread, and enter the "Task Queue" (task queue) of tasks, only "task queue" notify the main thread, an asynchronous tasks can be carried out, the task will enter the main thread.

In particular, the operating mechanism of asynchronous execution is as follows (synchronous execution, too, because it can not be regarded as asynchronous execution asynchronous tasks):

( 1) all the synchronization tasks are performed on the main thread, forming a "execution stack" (Execution Stack context); 
( other than 2) main thread, there is a "Task Queue" (task queue). With asynchronous task to run as long as the result, in place of an event "Task Queue" Among;  ( 3) Once the "execution stack" of all the synchronization task is completed, the system will remove the "Task Queue" corresponding event callback function into the "execution stack" started; ( 4) the main thread is repeated the third step above.

In addition to defining the broad sense, we can be more precise definition of the task, the task is divided into macro and micro tasks:

  • Macrotask (macro-task):  including the overall codes script, setTimeout, setInterval, ajax, dom operation
  • Micro-task (Micro-Task):  Promise

In particular, the operating mechanism of macro and micro task execution tasks as follows:

1)首先,将"执行栈"最开始的所有同步代码(宏任务)执行完成;
(2)检查是否有微任务,如有则执行所有的微任务; (3)取出"任务队列"中事件所对应的回调函数(宏任务)进入"执行栈"并执行完成; (4)再检查是否有微任务,如有则执行所有的微任务; (5)主线程不断重复上面的(3)(4)步。

以上两种运行机制,主线程都从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)

setTimeout()、setInterval()

setTimeout() 和 setInterval() 这两个函数,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行。

setTimeout() 和 setInterval() 产生的任务是 异步任务,也属于 宏任务

setTimeout() 接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。setInterval() 接受两个参数,第一个是回调函数,第二个是反复执行的毫秒数。

如果将第二个参数设置为0或者不设置,意思 并不是立即执行,而是指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

所以说,setTimeout() 和 setInterval() 第二个参数设置的时间并不是绝对的,它需要根据当前代码最终执行的时间来确定的,简单来说,如果当前代码执行的时间(如执行200ms)超出了推迟执行(setTimeout(fn, 100))或反复执行的时间(setInterval(fn, 100)),那么setTimeout(fn, 100) 和 setTimeout(fn, 0) 也就没有区别了,setInterval(fn, 100) 和 setInterval(fn, 0) 也就没有区别了。

Promise

Promise 相对来说就比较特殊了,在 new Promise() 中传入的回调函数是会 立即执行 的,但是它的 then() 方法是在 执行栈之后,任务队列之前 执行的,它属于 微任务

process.nextTick

process.nextTick 是 Node.js 提供的一个与"任务队列"有关的方法,它产生的任务是放在 执行栈的尾部,并不属于 宏任务 和 微任务,因此它的任务 总是发生在所有异步任务之前。

setImmediate

setImmediate 是 Node.js 提供的另一个与"任务队列"有关的方法,它产生的任务追加到"任务队列"的尾部,它和 setTimeout(fn, 0) 很像,但优先级都是 setTimeout 优先于 setImmediate。

有时候,setTimeout 的执行顺序会在 setImmediate 的前面,有时候会在 setImmediate 的后面,这并不是 node.js 的 bug,这是因为虽然 setTimeout 第二个参数设置为0或者不设置,但是 setTimeout 源码中,会指定一个具体的毫秒数(node为1ms,浏览器为4ms),而由于当前代码执行时间受到执行环境的影响,执行时间有所起伏,如果当前执行的代码小于这个指定的值时,setTimeout 还没到推迟执行的时间,自然就先执行 setImmediate 了,如果当前执行的代码超过这个指定的值时,setTimeout 就会先于 setImmediate 执行。

优先级

通过上面的介绍,我们就可以得出一个代码执行的优先级:

同步代码(宏任务) > process.nextTick > Promise(微任务)> setTimeout(fn)、setInterval(fn)(宏任务)> setImmediate(宏任务)> setTimeout(fn, time)、setInterval(fn, time),其中time>0

代码解析

回到开头给出的代码,我们来一步一步解析:

运行"执行栈"中的代码:

console.log(1); // setTimeout(function () { // 作为宏任务,暂不执行,放到任务队列中(1) // console.log(2); // // new Promise(function (resolve, reject) { // console.log(3); // resolve(); // console.log(4); // }).then(function () { // console.log(5); // }); // }); function fn() { console.log(6); //setTimeout(function () { // 作为宏任务,暂不执行,放到任务队列中(3) // console.log(7); //}, 50); } new Promise(function (resolve, reject) { console.log(8); resolve(); console.log(9); }) // .then(function () { // 作为微任务,暂不执行 // console.log(10); // }); fn(); console.log(11); process.nextTick(function () { console.log(12); }); // setImmediate(function () { // 作为宏任务,暂不执行,放到任务队列中(2) // console.log(13); // });

此时输出为:1 8 9 6 11 12

运行微任务:

new Promise(function (resolve, reject) { // console.log(8); // 已执行 // resolve(); // 已执行 // console.log(9); // 已执行 }) .then(function () { console.log(10); });

此时输出为:10

读取"任务队列"的回调函数到"执行栈"

setTimeout(function () { console.log(2); new Promise(function (resolve, reject) { console.log(3); resolve(); console.log(4); }) //.then(function () { // 作为微任务,暂不执行 // console.log(5); //}); });

此时输出为:2 3 4

再运行微任务:

setTimeout(function () { // console.log(2); // 已执行 new Promise(function (resolve, reject) { // console.log(3); // 已执行 // resolve(); // 已执行 // console.log(4); // 已执行 }) .then(function () { console.log(5); }); });

此时输出为:5

再读取"任务队列"的回调函数到"执行栈"

setImmediate(function () { console.log(13); });

At this time, the output is:13

Run micro tasks:

no

Then read "Task Queue" callback function to "execution stack" :

// function fn () {// executed
     // console.log (6); // executed
     the setTimeout ( function ( ) {Console . Log ( . 7 ) ; } , 50 ) ; //}

At this time, the output is:7

Run micro tasks:

no

In summary, the final output sequence is:1 8 9 6 11 12 10 2 3 4 5 13 7

 

Guess you like

Origin www.cnblogs.com/leftJS/p/11070104.html