First, js single thread problems

Js is single-threaded and processes tasks one after another, so if one task takes a long time to process, subsequent tasks will block

Therefore, JS solves this problem through the Event Loop. Before understanding the Event Loop, we need to know some keywords

What are stack, Queue, heap, event loop

  • Stack: Throw up if you eat too much
  • Queue: eating too much… The release of
  • Heap: Stores OBJ objects

Execution stack

When the JS engine runs, when the code starts to run, it pushes the code onto the execution stack for execution

Ex. :

When the code is parsed, the functions are pushed on the stack in turn

If you push it, you push it out, and when c is done, you start pushing it out

When the execution stack encounters asynchrony

It’s in and out, but it’s synchronous, and synchronous means it blocks, so it needs to be asynchronous, so what happens when there’s asynchronous code in the stack

At this point in the code, add the click event and setTimeout, and now observe the execution order

Observe the stack effect at this point, and the above function nesting is significantly different

1, console.log(“sync”) statements are not pushed to the bottom of the stack because the console is finished

2, Click and setTimeout are both pushed, but their internal console is not pushed, indicating that they are not finished executing

3. If click is not completed, why is setTimeout pushed and should not be blocked?

The answer is: when the browser finds an asynchronous task in the execution stack, it hands it off to WebAPI for maintenance, and the execution stack continues to perform subsequent tasks

Also, setTimeout will be added to the WebAPI

Webapi is a browser implementation that maintains events.

Above, there is a progress bar next to setTimeout. This progress is the set waiting time

Callback queue

In the example above, when the setTimeout execution ends, should we go back to the execution stack and execute the output?

Answer: Not really!

At this point, the setTimeout executable after the countdown ends is put into the callback queue

Finally, the setTimeout executable is removed from the callback queue and placed on the execution stack again

This execution is called an event loop

The detailed process of Event Loop

A task is fetched from the head of the callback queue after the stack task is cleared

Here is the simplest example, with the output 1,3,2

Why is that?

The figure above shows the order of execution:

1, console.log (1) is pushed onto the execution stack

SetTimeout is recognized as an asynchronous task in the execution stack and put into webapis

Console.log (3) is pushed onto the execution stack while the setTimeout executable waits in the callback queue

4, console.log(3) After execution, fetch console.log(2) from the head of the callback queue and put it on the execution stack

5, console.log(2) execute

Callback queue first in first out

Note that the callback queue is first-in, first-out, for example:

When console.log(4) completes, console.log(2) is retrieved from the callback queue;

Note: Console.log (3) will not be fetched from the callback queue until the execution of console.log(2) is complete and the stack is emptied again.

Test if the concept is correct

The code above finally prints 1,5,2,4,3, and executes:

1, prints 1, and pushes 2 into the callback queue

2. Push 4 into the callback queue

3, output 5

4, clears the stack, reads output 2, finds 3, and pushes 3into the callback queue

5, clears the stack of execution and reads output 4

6, clears the stack of execution and reads output 3

At this point, it seems to be ok, but !!!!!! The matter is not over yet

Macrotask, Microtask

Now that you know a little bit about Event loop from the example above, let’s move on to another example

console.log(1);
setTimeout(()=>{
	console.log(2)
})
var p = new Promise((resolve,reject)=>{
	console.log(3)
	resolve("Success")
})
p.then(()=>{
	console.log(4)
})
console.log(5)
Copy the code

According to the concept of event loop, it would be 1,3,5,2,4, because setTimeout and then would be placed in the callback queue, and then first in, first out again, so it would be 2 and 4

But the order of facts is 1,3,5,4,2!

This is because promise’s then method is considered to be in a Microtask queue

What is a Macrotask?

A Macrotask is a callback queue

What are microtasks?

A Microtask is also a queue of tasks that are executed in order after the stack has been emptied

I’m going to graph it

You can see the Macrotask the callback queue and then there’s a Microtask on top of it

Microtasks are queued, but they are not placed one by one on the execution stack. When the execution stack is empty, all the tasks in the Microtask queue are executed, and the first Macrotask in the callback queue is fetched last.

Ex. :

The above execution is as follows:

1. Assign setTimeout to the macro task push

2. Push THEN (2) into the microtask

3. Push THEN (4) into the microtask

4. If the task queue is empty, take the first then(2) of the micro-task and push it into the execution stack

5, output 2, push then(3) into the microtask

6. If the task queue is empty, take the first then(4) of the micro-task and push it into the execution stack

7, output 4

8. If the task queue is empty, take the first then(3) of the micro-task and push it into the execution stack

9, output 3

The task queue is empty, so is the microtask.

11, output 1

Why is then a microtask

This depends on each browser, and each browser implements different promises. Some THEN are macro tasks, some are micro-tasks, and Chrome is micro-tasks, which generally default to micro-tasks

In addition to then, several other events are also recorded as microtasks:

  • process.nextTick
  • promises
  • Object.observe
  • MutationObserver
console.log("start");
setImmediate(()=>{
    console.log(1)
})
Promise.resolve().then(()=>{
    console.log(4);
})
Promise.resolve().then(()=>{
    console.log(5);
})
process.nextTick(function foo() {
    console.log(2);
});
process.nextTick(function foo() {
    console.log(3);
});
console.log("end")
Copy the code

The code above prints start,end,2,3,4,5,1

Process. nextTick is not the same concept as then. It is added to the bottom of the execution stack, so it does not behave the same way

Final test

console.log("1");
setTimeout(()=>{
    console.log(2)
    Promise.resolve().then(()=>{
        console.log(3);
        process.nextTick(function foo() {
            console.log(4);
        });
    })
})
Promise.resolve().then(()=>{
    console.log(5);    
    setTimeout(()=>{
        console.log(6)
    })
    Promise.resolve().then(()=>{
        console.log(7);
    })
})

process.nextTick(function foo() {
    console.log(8);
    process.nextTick(function foo() {
        console.log(9);
    });
});
console.log("10")
Copy the code

Order of execution:

1, output 1

2. Push setTimeout (2) into the macro task

3. Push THEN (5) into the microtask

4. Add nextTick to bottom of stack (8)

5, output 10

6. Execute nextTick (8)

7, output 8

Add nextTick (9) at the bottom of the stack

9, output 9

10, Perform microtasks then (5)

11, output 5

Push setTimeout (6) into the macro task

13. Push THEN (7) into the microtask

14, Perform microtasks then (7)

15, output 7

SetTimeout (2)

17, output 2

18. Push THEN (3) into the microtask

19, Perform microtasks then (3)

20, output 3

21. Add nextTick (4) at the bottom of the stack

22, output 4

SetTimeout (6)

24, output 6

The final results were: 1,10,8,9,5,7,2,3,4,6

reference

  • Concurrency model and event loop
  • Youtube video