preface

Because there is a certain limit to the number of words after the revision of the gold nugget (pro test under about 12,500 words, So see title there are tens of thousands of words long title must be pulling your 😂) after beautification design layout on word is beyond the limit so going to the back of the part was singled out to write, it is better to write a relatively in-depth content, for this kind of front-end system 】 【 content must be including but not limited to the title, I will go as far and as deep as possible to write good articles of high quality.

Online humble, if you think this article is helpful to you welcome everyone to click 👻

A question leads to the thinking of Event Loop

There are too many concepts involved in Event Loop(Event polling), and it is too boring to talk about a lot of conceptual things and follow my ideas from the beginning, so I plan to write this article in a different way. You should first solve this problem according to your previous understanding of Event Loop(Event polling). I’ll write down the way I think about this in terms of the Event Loop. Two different understandings, ideas, collision, I may understand wrong, you may have some knowledge points ignored before, we

Sorry about the wrong 😅, this picture

The title

console.log('script start');

setTimeout(() = > {
  console.log('north song');
}, 1 * 2000);

Promise.resolve()
.then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});


async function foo() {
  await bar()
  console.log('async1 end')
}
foo()

async function errorFunc () {
  try {
    await Promise.reject('error!!! ')}catch(e) {
    console.log(e)
  }
  console.log('async1');
  return Promise.resolve('async1 success')
}
errorFunc().then(res= > console.log(res))

function bar() {
  console.log('async2 end')}console.log('script end');
Copy the code

All right, so I’m not going to scroll down, but I’m going to try to solve this problem as you understand it.




— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — I’m line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —



I believe this problem is certainly not difficult to everyone, but we are in accordance with what kind of way to solve this problem? In fact, this question tests you a lot of knowledge, and I will use my understanding to say to think about this problem. The level is limited and any questions are welcome to point out in the comments section.

JS runtime mechanism

Let’s begin by explaining the meanings of the words in the picture above.

Heap, Stack, Queue, Event Loop

A stack queue in a program

Heap (Heap)

Heap, is a dynamic storage structure, is the use of complete binary tree maintenance of a set of data, heap is divided into two kinds, one is the maximum heap, a minimum heap, the root node of the largest heap is called the maximum heap or large root heap, the root node of the smallest heap is called the minimum heap or small root heap. A heap is a linear data structure, equivalent to a one-dimensional array, with a unique successor.

Such as the maximum heap

The Stack (Stack)

The stack setting in the program is a linear table that is restricted to inserting or deleting only at the end of the table. A stack is a data structure that followsLast in, first out(LIFO: last-in-first-out), the first data is pushed to the bottom of the stack, and the last data is pushed to the top of the stack. When the data needs to be read, the data is popped from the top. A stack is a special linear table that can only be inserted and deleted at one end.

Queue

What makes a queue special is that it allows only deletes at the front of the table and inserts at the rear. Like a stack, a queue is a linear table with limited operations. The end that performs the insert operation is called the tail of the queue, and the end that performs the delete operation is called the head of the queue. When there are no elements in the queue, it is called an empty queue.

The data elements of a queue are also called queue elements. Inserting a queue element into a queue is called enqueuing, and removing a queue element from a queue is called dequeuing. Since a queue can only be inserted at one end and deleted at the other end, only the earliest elements in the queue can be deleted from the queue firstFirst in first out(FIFO: first-in-first-out)

Js stack queue

Let me explain the JavaScript language heap, stack, queue.

The heap

Heap, dynamically allocated memory, variable size and not automatically freed, holding reference types, objects that may consist of multiple values, stored in the heap, containing variables of reference types, actually holding not the variable itself, but a pointer to the object. Can be understood simply as storing blocks of code.

What the heap does: store data for reference type values

let obj = {
    name: 'north song'.puslic: 'Front End Self-taught Post'
}

let func = () = > {
    console.log('hello world')}Copy the code

The stack

The Stack in JS should be called the call Stack (EC Stack), will automatically allocate memory space, will automatically release, stored basic types, simple data segments, occupy a fixed size of space.

Stacks are used to store values of basic types and have another important function. Provides an environment for code execution

The queue

The js queue can be called task queue or asynchronous queue, task queue to store a variety of asynchronous operation registered callback, which is divided into two types of task, macroTask () and microTask ().

All right, now we can get back to business.

Why do Event loops occur

It is well known that JS is a single threaded non-blocking scripting language, and Event Loop is a solution to solve JS asynchronous programming.

Why is JS a single threaded language, and how does it implement asynchronous (non-blocking) programming

First question: JavaScript was created to handle the interactions of web pages in the browser (handling DOM operations, UI animations, etc.). The reason it’s designed to be single-threaded is because you don’t want to make the browser too complicated. This is too complicated for a web scripting language because multiple threads need to share resources and potentially modify each other’s results (two threads modifying the same DOM node can cause unnecessary trouble).

Second problem: JavaScript is single-threaded but the host environment in which it runs – the browser is multi-threaded, and the browser provides various threads for Event Loop scheduling to coordinate the JS single-threaded run without blocking.

summary

To summarize a wave of personal understanding of how JS works:

Code execution on a global call stack stack (main) provide code running environment, synchronization task during the implementation of code executed immediately, confronted with a task asynchronous asynchronous callback to registration to the task queue, waiting to see if the asynchronous synchronization code has been completed, if the current asynchronous task callback to the stack

Processes and Threads

Process: A process is the smallest unit of CPU resources allocated (it is the smallest unit that can hold resources and run independently)

Thread: A thread is the minimum unit of CPU scheduling (a thread is a unit of one-time program running based on a process).

There is no exact and uniform description of processes and threads. It can be understood simply:

For example, an application: such as QQ, browser startup will open a process, and the process can have multiple threads to carry out resource scheduling and allocation, to run the role of the program.

More colloquial: open the QQ application to start the process to run the program (QQ), there are multiple threads for resource scheduling and allocation (multiple threads to allocate open QQ occupied storage), to run the program (QQ).

Take an operating system as an example:

Threads depend on processes. A process can have one or more threads, but threads can only belong to one process.

JS single thread

Js single-threaded means that the javaScript engine has only one thread

Single threading means that all tasks need to be queued until the previous task finishes before the next task is executed. If the first task takes a long time, the second will have to wait. The JS engine executes asynchronous code without waiting because there are task queues and event polling.

  • Task queue: A task queue is a first-in, first-out (FIFO) queue that holds various task callbacks.
  • Event polling: Event polling is the process of the main thread repeatedly fetching tasks from the task queue and executing them.

Multithreading in browsers

  1. GUI rendering thread
    • Draw pages, parse HTML, CSS, build DOM trees, layout and draw
    • Page redraw and backflow
    • Mutually exclusive with JS engine threads, so called JS performs blocked page updates
  2. JS engine thread
    • Responsible for execution of JS script code
    • Responsible for quasi-execution is the event that is ready for execution, that is, the event that the timer count ends, or the asynchronous request succeeds and is returned correctly
    • It is mutually exclusive with the GUI rendering thread and takes too long to execute will block rendering of the page
  3. Event-triggered thread
    • Responsible for handing the prepared events to the JS engine thread for execution
    • When multiple events are added to the task queue, they need to be queued (JS single thread).
  4. Timer triggered thread
    • Responsible for the execution of asynchronous timer class events such as setTimeout, setInterval
    • After the timer expires, the registered callback is added to the end of the task queue
  5. HTTP request thread
    • Responsible for executing asynchronous requests
    • When the main thread executes an asynchronous request, it assigns the function to the thread. When it listens for a state change event, if there is a callback function, the thread will add the callback function to the end of the task queue for execution

Event Loop

Whew, back to business at last!

For event polling the above is actually quite clear:

Event polling is a core mechanism that solves some of the drawbacks of javaScript single threading for asynchronous operations. It is used to coordinate various events, user interactions, script execution, UI rendering, network requests, and so on.

Eveent Loop execution order in the browser

The Processing Model specification defines the process of an Eveent Loop:

As long as an Eveent Loop exists, the following steps will continue:

  • 1. Select the oldest task in the Tasks queue. The user agent can select any task queue.
  • 2. Set the selected task to the running task.
  • 3.Run: Run the selected task.
  • 4. Change the Eveent Loop currently running Task to NULL.
  • 5. Remove previously running tasks from the task queue.
  • 6.Microtasks: Perform Microtasks checkpoint. (That is, executing tasks in the MicroTasks queue)
  • 7. Update the Rendering: Can be simply interpreted as browser rendering…
  • If this is a worker Event loop, but no task is in the task queue, and the WorkerGlobalScope object’s closing identifier is true, then the Eveent loop is destroyed and these steps are terminated. Then define run a worker in the Web Workers section.
  • 9. Go back to step 1.

Eveent Loopp loops through the above steps over and over again, in a nutshell:

  • Eveent LoopIt’s going to go round and roundtasksThe oldest task in the queue is pushed onto the stack and is executed in turn and cleared in the loopmicrotaskTasks in the queue.
  • aftermicrotaskThe tasks in the queue, there aremayUpdates will be rendered. (The browser is smart. Instead of responding to multiple DOM changes within a frame, the browser accumulates the changes and updates the view at up to 60HZ(about 16.7ms per frame).)

Macrotask and microtask priority problems

Asynchronous callbacks registered in a queue fall into two types, macrotasks and microtasks. For convenience, we can think of macro task queue and micro task queue in the task queue. There are multiple macro task queues and only one microtask

  • Macro Tasks

    • Script (overall code)
    • setTimeout/setInterval
    • SetImmediate (environment)
    • The UI rendering
    • requestAnimationFrame
    • .
  • Micro Task

    • The callbacks in then(), catch(), finally() of a Promise

    • Process. NextTick (Node environment)

    • .

Personal understanding of the order of execution:

  1. The code from the start of execution calls a global execution stack, and the script tag executes as a macro task

  2. In the process of execution, the synchronous code is executed immediately, and the asynchronous code is put into the task queue. The task queue contains two types of asynchronous tasks, macro task queue and micro task queue.

  3. When the synchronized code executes, that means the first macro task executes (script)

    • 1. Check whether microtasks generated during the execution of macro tasks exist in the microtask queue

      1-1, if any, empty all microtasks in the queue

      2-2. The microtasks generated during the execution of the microtask are put into the microtask queue and cleared in this execution

    • 2. If there is no macro task in the macro task queue, execute it if there is. If there is no macro task, the first wave of event polling ends

      2-1. Put the microtasks generated during execution into the microtask queue

      2-2. Execute the code to clear the microtask queue after completing the macro task

Therefore, the macro task takes precedence, and only after the macro task is executed will all microtasks in the task queue be cleared at one time.

Solve the problem analysis process

Let’s take the first one down

// the macro task is executed as soon as the code is executed
console.log('script start'); 

setTimeout(() = > { 1 / / macro
  console.log('north song');
}, 1 * 2000);

Promise.resolve()
    .then(function() { Micro / / 1-1
      console.log('promise1');
    })
    .then(function() { // select * from 'then' where 'then' = '1' and 'then' = '1' and 'then' = '1';
      console.log('promise2'); 
    });


async function foo() {
  await bar() // => await(the syntax sugar of the promise), waiting asynchronously for the return value
  // => The following code can be interpreted as an asynchronous queue microtask. I'll leave it to you to get into more detail
  console.log('async1 end') Micro / / 1 to 2
}
foo()

function bar() {
  console.log('async2 end')}async function errorFunc () {
  try {
    await Promise.reject('error!!! ')}catch(e) {
      // => All code from this point on can be understood as put into the asynchronous queue microtask
    console.log(e)  // 微1-3
  }
  console.log('async1');
  return Promise.resolve('async1 success')
}
errorFunc().then(res= > console.log(res)) Micro / / 1-5

console.log('script end');
Copy the code

I won’t go into how the above code uses promises; I’ll write about Promise source parsing later.

Note that promise.then ().then(). When registering an asynchronous task, the second THEN callback depends on the result of the first THEN callback. If there is no exception, the state callback will be registered after the asynchronous task has completed

First execution

Globally a macro task executes, output synchronization code. Mount macro 1, Micro 1-1, micro 1-2, micro 1-3, micro 1-4. 1- Indicates the first poll

Run: script start, async2 end, and script endCopy the code

Second execution

After the synchronous code is executed, the code in the microtask queue in the asynchronous task is executed.

Microtask queue: There is only one queue and it is cleared once in the current poll

Run: execution1-1: Promise1 executes micro1-2: async1 end Executes micro1-3: error!!! And async1. Only after the current asynchronous callback is completedPromise.resolve('async1 success'), and register the successful callback - micro in then()1-5Executing the1-4Promise2: Execute the newly registered micro1-5: async1 success
Copy the code

By the end of this first round of polling

Third execution

Enable the second round of polling: Execute macro 1

run: 'north song'
Copy the code

To this. The polling is complete.

In fact, it is relatively difficult to understand the microtasks, for which only one queue will be emptied in this poll (including the microtasks generated during the execution).

For example, 🌰

You go to the diner and wait for a meal. Originally, you only planned to eat two dishes today (only two call-back were registered in the microtask queue). When you see your favorite dish, braise pork in soy sauce (a new microtask encountered during the execution of the microtask), you must add another dish (add microtask to the microtask queue)

Drawing analysis

I believe that through the above explanation you should be able to understand, in order to give you a deeper understanding, to form a strong impression of the scene, I drew a GIF

I’ve posted the code for your convenience

Have advanced

With the above explanation, you can now brush a few questions to see how you hold it. (Update: The parsing is a bit longer. You can write it yourself and see it again.)

Rules now, just to make it easier for you to understand, remember the rules:

  • The analysis is done on a per-poll basis, and the synchronization block outputs the results directly
  • In the asynchronous task code block, red represents macro tasks and green represents microtasks
  • The 1 -Represents all microtasks in the microtask queue in the first polling,The 2 -The second time, and so on

Gold question

console.log('1');

setTimeout(() = > {
  console.log('2');
  Promise.resolve().then(() = > {
    console.log('3');
  })
  new Promise((resolve) = > {
    console.log('4');
    resolve();
  }).then(() = > {
    console.log('5')})})Promise.reject().then(() = > {
  console.log('13');
}, () = > {
  console.log('12');
})

new Promise((resolve) = > {
  console.log('7');
  resolve();
}).then(() = > {
  console.log('8')})setTimeout(() = > {
  console.log('9');
  Promise.resolve().then(() = > {
    console.log('10');
  })
  new Promise((resolve) = > {
    console.log('11');
    resolve();
  }).then(() = > {
    console.log('12')})})Copy the code

I’m not going to parse this because it’s a little bit easier.

Masonry topic

new Promise((resolve, reject) = > {
  console.log(1)
  resolve()
})
.then(() = > { Micro / / 1-1
  console.log(2)
  new Promise((resolve, reject) = > {
      console.log(3)
      setTimeout(() = > { 2 / / macro
        reject();
      }, 3 * 1000);
      resolve() / / TODO note 1
  })
    .then(() = > { // Note 2
      console.log(4)
      new Promise((resolve, reject) = > {
          console.log(5)
          resolve();
      })
        .then(() = > { Micro / / 1-4
          console.log(7)
        })
        .then(() = > { Micro / / 1-6
          console.log(9)
        })
    })
    .then(() = > {  // Note 3
      console.log(8)
    })
})
.then(() = > { // 微1-3
  console.log(6=)})Copy the code

Resolution:

The first poll

The script tag (macro 0) executes

Output synchronization code:

1
Copy the code

To mount an asynchronous task:

Console.log (2) New Promise((resolve, ()) new Promise((resolve, ()) reject) => { console.log(3) setTimeout(() => { reject(); }, 3 * 1000); resolve() }) .then(() => { console.log(4) new Promise((resolve, reject) => { console.log(5) resolve(); }) .then(() => { console.log(7) }) .then(() => { console.log(9) }) }) .then(() => { console.log(8) }) }Copy the code

The first macro task is completed at the same time as the synchronization code is finished, and the task queue in the asynchronous task is cleared:

micro1-1: 2,3   
Copy the code

Note 1

This is very depressing. Once the promise state is changed, it cannot be changed (the active discard can still affect the next then state). The reject state has not been executed yet, so the new promise state in Micro1-1 is fulfilled.

Note 2

After the newPromise in micro1-1 determines the state, the first callback in the first THEN can be registered with the asynchronous task, but the second callback is not registered yet, and after the micro1-2 is mounted, the micro1-1 callback executes with no task exception, and the state continues to hang in the outermost THEN (1-3).

A new microtask generated during the execution of the microtask will be put into the microtask queue, and the microtask will also be cleared in this poll:

() => {// 微1-2 console.log(4) new Promise((resolve, reject) => {console.log(5) resolve(); }). Then (() = > {the console. The log (7)}), then (() = > {the console. The log (9)})}) = > {1-3 console. / / micro log (6)}Copy the code

The macro tasks generated during the execution of the microtask are placed in the new macro task queue:

() = > {2 / / macro
	reject();
 }
Copy the code

Start executing a microtask The microtasks that are generated during execution:

  • First execute micro 1-2
micro1-2:4,5
Copy the code

If a new microtask is created in microtask 1-2 and placed in the queue, it will also be cleared in this poll, but you will have to wait for the previous microtask to complete (good for first-in, first-out):

() = > {1-4 console. / / micro log (7)} () = > {1-5 console. / / micro log (8)}Copy the code

Note 2

The status of the first callback in the resolve() step is determined after the resolve() step in micro2, registering the first callback in micro1-4, but the second callback is not yet suspended. Because he has not executed the above callback (can you be sure that the above “then” did not throw the wrong initiative? So two then also does not necessarily register the successful callback), to this win micro 1-2 execute finished, state downward continuation micro 1-2then below the then can register the successful callback, register micro 1-5

  • Executing the 1 to 3
micro1-3: 6
Copy the code

After the micro 1-3 execution is complete, the micro tasks generated during the micro 1-2 execution can be executed

  • Executing the 1-4
micro1-4: 7
Copy the code

If a new microtask is created by executing microtasks 1-4 and placed in the queue, it will also be cleared in this poll, but you will have to wait for the previous microtask to complete (a good example of a first-in, first-out queue):

() => {// micro 1-6 console.log(9)}Copy the code
  • Executing the 1 to 5
micro1-5: 8
Copy the code

After the micro 1-5 execution is complete, you can perform the micro tasks generated during the micro 1-4 execution

micro1-6: 9
Copy the code

At this point, the microtask is finally cleared. The output results are as follows:

Sync code:11-1: 2,31-2:4,51-3: 61-4: 71-5: 81-6: 9
Copy the code

The second poll

Open macro 2, this code

() = > {2 / / macro
	reject();
}
Copy the code

The then state is already determined, and this code does not use a hammer.

Output results:

1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
Copy the code

King problem

Promise.resolve()
  .then(() = > {
    console.log('promise1');
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
          console.log('timer2')
          resolve()
        }, 0)
    })
      .then(async() = > {await foo();
        return new Error('error1')
      })
      .then((ret) = > {
        setTimeout(() = > {
          console.log(ret);
          Promise.resolve()
          .then(() = > {
            return new Error('error!!! ')
          })
          .then(res= > {
            console.log("then: ", res)
          })
          .catch(err= > {
            console.log("catch: ", err)
          })
        }, 1 * 3000)},err= > {
        console.log(err);
      })
      .finally((res) = > {
        console.log(res);
        throw new Error('error2')
      })
      .then((res) = > {
        console.log(res);
      }, err= > {
        console.log(err);
      })
  })
  .then(() = > {
    console.log('promise2');
  })

function foo() {
  setTimeout(() = > { 
    console.log('async1');
  }, 2 * 1000);
}

setTimeout(() = > {
  console.log('timer1')
  Promise.resolve()
    .then(() = > {
      console.log('promise3')})},0)

console.log('start');
Copy the code

This problem and the most powerful king of the problem is similar, I first use this way of analysis, the following problem has every step of the analysis, otherwise the word count will exceed (suggested in the IDE analysis) :

Promise.resolve()
  .then(() = > { Micro / / 1-1
    console.log('promise1');
    return new Promise((resolve, reject) = > {
        setTimeout(() = > { 3 / / macro
          console.log('timer2') 
          resolve() // TODO is asynchronously confirming the Promise's state,
                    // TODO so the following then callbacks are not registered in the asynchronous task when the state has not been determined
        }, 0)
    })
      .then(async() = > {/ / micro 3 to 1
        // TODO look first
        // await expression simply means' wait asynchronously 'to get the result,
        // await is to optimize the then chain writing of 'Promise' directly to help you asynchronously wait for the result
        Foo () => promise.then ((res) => res)
        Then (() => {return new Error('error1')})

        console.log( await foo()) // TODO = "await"; // TODO = "await"
        // TODO look again
        // Await expression needs to be obtained asynchronously. The code below the await expression is equivalent to hanging in an asynchronous queued microtask
        // Wait asynchronously until the result is obtained
        return new Error('error1') / / micro 4-1
      }) 
      .then((ret) = > {  / / micro 4-2
        setTimeout(() = > { 5 / / macro
          console.log(ret);
          Promise.resolve()
          .then(() = > { / / micro 5-1
            return new Error('error!!! ')
          })
          .then(res= > { / / micro 5-2
            console.log("then: ", res)
          })
          .catch(err= > {
            console.log("catch: ", err)
          })
        }, 1 * 3000)},err= > {
        console.log(err);
      })
      .finally((res) = > { // The first state of micro 3-2 is uncertain, but finally executes regardless of the state and does not accept the task parameters
        console.log(res);
        throw new Error('error2')
      })
      .then((res) = > { / / micro 3-3
        console.log(res);
      }, err= > {
        console.log(err);
      })
  })
  .then(() = > { / / micro 3-4
    console.log('promise2');
  })

async function foo() {
  setTimeout(() = > { 4 / / macro
    console.log('async1');
  }, 2 * 1000);
  return Promise.resolve(1)}setTimeout(() = > { 2 / / macro
  console.log('timer1')
  Promise.resolve()
    .then(() = > { Micro / / 2 to 1
      console.log('promise3')})},0)

console.log('start');

/ * * * TODO micro 1 - said the first time in the polling tasks first polling * * * code first loaded script function of macro task execution: * mount asynchronous tasks: micro macro 2 * 1-1 output: start * * macros: task has been completed Start to execute micro task list, micro 1-1 * mount asynchronous task: macro 3 * output: * * * * second round of poll * first execute macro task: macro 2 * mount asynchronous task: micro 2-1 * output: timer1 * * macro task execution completed: Start to execute micro task list, micro 2-1 * mount asynchronous task: none * output: promise3 * * * * * Third round of polling * execute macro task first: macro 3 * mount asynchronous task: micro 3-1 micro 3-2 micro 3-3 micro 3-4 * output: Timer2 * * Macro task execution completed: start to execute microtask list, micro 3-1 micro 3-2 micro 3-3 micro 3-4 * mount asynchronous task: macro 4 * output: Undefined error2 promise2 * * * * undefined error2 promise2 * * * * undefined error2 promise2 * * * * Start with the microtask list, micro4-1 * mount the asynchronous task: micro4-2 * Output: no output, now the callBack is registered in the undefined state and is called in the poll, which is a good example of how the callBack is suspended in the asynchronous state. Macro 5 * mount asynchronous task: micro 4-1 * Output: error1 then: error1!! * /


// start ->promise1 -> timer1 -> promise3 -> timer2 -> undefined -> error2 ->
// promise2 -> async1 -> error1 -> then: error!!!

Copy the code

The glory of the king

So let’s do the last problem together.

async function async1() {
  console.log('async1 start');
  new Promise((resolve, reject) = > {
    try {
      throw new Error('error1')}catch(e) {
      console.log(e);
    }
    setTimeout(() = > { 3 / / macro
      resolve('promise4')},3 * 1000);
  })
    .then((res) = > { / / micro 3 to 1
      console.log(res);
    }, err= > {
      console.log(err);
    })
    .finally(res= > { // Note 3
      console.log(res);
    })
  console.log(await async2()); // Micro 4-1 TODO- Note 1
  console.log('async1 end'); // Micro 4-2 // TODO- note 2
}

function async2() {
  console.log('async2');
  return new Promise((resolve) = > {
    setTimeout(() = > { 4 / / macro
      resolve(2)},1 * 3000); })}console.log('script start');

setTimeout(() = > { 2 / / macro
  console.log('setTimeout');
}, 0)

async1();

new Promise((resolve) = > {
  console.log('promise1');
  resolve();
})
  .then(() = > { Micro / / 1 to 2
    console.log('promise2');
    return new Promise((resolve) = > {
      resolve()
    })
      .then(() = > { // 微1-3
        console.log('then 1-1')
      })
  })
  .then(() = > { Micro / / 1-4
    console.log('promise3');
  })


console.log('script end');
Copy the code

provisions

Now, just to make it easier for you to understand, remember the rules:

  • The analysis is done on a per-poll basis, and the synchronization block outputs the results directly
  • In the asynchronous task code block, red represents macro tasks and green represents microtasks
  • The 1 -Represents all microtasks in the microtask queue in the first polling,The 2 -The second time, and so on

The first poll

The script tag (macro 0) executes

Output synchronization code:

script start -> async1 start -> error1 -> async2 -> promise1 -> script end
Copy the code

To mount an asynchronous task:

-() => {// macro 2
- console.log('setTimeout');
-}

-() => {// macro 3
- resolve('promise4')
-}

-() => {// macro 4
- resolve(2)
-}


+() => {// micro 1-1
+ console.log('promise2');
+ return new Promise((resolve) => {
+ resolve()
+}
Copy the code

The first macro task is completed at the same time as the synchronization code is finished, and the task queue in the asynchronous task is cleared:

micro1-1: promise2 - >1-2: then 1-1Micro - >1-3: promise3
Copy the code

A new microtask generated during the execution of the microtask will be put into the microtask queue, and the microtask will also be cleared in this poll:

+() => {// micro 1-2
+ console.log('then 1-1')
+}

+() => {// micro 1-3
+ console.log('promise3');
+}
Copy the code

The macro tasks generated during the execution of the microtask are placed in the new macro task queue:

No new macro task was created during this microtask executionCopy the code

Note 1

“Await” is the syntax sugar of promises. The internal implementation also relies on promises. It was created to optimize the “then” chain of promises, to write asynchronous code in a synchronous manner. The expression of await is equivalent to calling a THEN method that returns a promise, asynchronously (waiting) to get its return value. That await < = = > promise. Then

The async2 function here opens a macro task in the Promise returned by the async2 function, and the await asynchronous waits for the macro task to execute before getting the return value. That is, the macro task cannot call the Promise’s THEN method at all without performing the await expression

Note 2

The code following the “await” expression can be simply understood as being put into a microtask, but the “await” expression does not retrieve the result of the asynchronous wait at all. The code following the “await” expression is not mounted to the asynchronous task after it jumps out of the current execution stack. Some tutorials say that the code following the await expression can be regarded as the first of the microtask queue. This is incorrect!

After the polling is complete, the following output is displayed:

Script start -> async1 start -> error1 -> async2 -> promise1 -> script end micro1-1: promise2 - >1-2: then 1-1Micro - >1-3: promise3
Copy the code

The second poll

The first poll is complete. The second poll is started

Execute the second macro task queue (the macro task queue holds only one macro task) :

The macro2:setTimeout
Copy the code

After the macro task is executed, no new microtask or macro task is created. The second poll is complete

After the polling is complete, the following output is displayed:

2: macro setTimeoutCopy the code

The third round is performed

Execute the third macro task queue (the macro task queue holds only one macro task) :

The macro task itself doesn't output anything, but let's make surePromiseAnd passed a'promise4'Give a successful callback to the next THENCopy the code

New microtasks generated during the execution of macro tasks are placed in the microtask queue:

+(res) => {// micro 3-1
+ console.log(res);
+}
Copy the code

Start to clear the microtask queue in the asynchronous task after the macro task is executed:

micro3-1: promise4 - >3-2: undefined
Copy the code

A new microtask generated during the execution of the microtask will be put into the microtask queue, and the microtask will also be cleared in this poll:

+res => {// micro 3-2 // TODO
+ console.log(res);
+}
Copy the code

The macro tasks generated during the execution of the microtask are placed in the new macro task queue:

No new macro task was created during this microtask executionCopy the code

After the polling is complete, the following output is displayed:

micro3-1: promise4 - >3-2: undefined
Copy the code

Note 3

As mentioned earlier, promise.finally() is also a microtask, finally can be understood as executing me regardless of the promise’s state of success or failure. But I don’t accept any result. Therefore, finally cannot accept the return value res as undefined

Fourth round

Execute the fourth macro task queue (the macro task queue holds only one macro task) :

The macro task itself doesn't output anything, but let's make surePromiseAnd passed a2Give a successful callback to the next THENCopy the code

New microtasks generated during the execution of macro tasks are placed in the microtask queue:

“Await => promise.then ()”, where the completion of the macro task determines that the Promise state can be used to retrieve asynchronously awaited results. Then ((res) => {return res}) and the code following the await expression is equivalent to waiting asynchronously for the result to be placed in the microtask queue. Promise.then((res) => {return res}).finally(() => {}), which is queued asynchronously in code only after the result is obtained in front of the await expression

An experiment can be done to set the value of the async wait timer to longer. The code behind the await expression does not respond until the result of the async wait is obtained.

+(res) => {return res} // micro 4-1

+() => {async1 end} // micro 4-2

Copy the code

Start to clear the microtask queue in the asynchronous task after the macro task is executed:

micro4-1: 2Micro - >4-2: async1 end
Copy the code

A new microtask generated during the execution of the microtask will be put into the microtask queue, and the microtask will also be cleared in this poll:

No new microtask was created during this microtask executionCopy the code

The macro tasks generated during the execution of the microtask are placed in the new macro task queue:

No new macro task was created during this microtask executionCopy the code

After the polling is complete, the following output is displayed:

micro4-1: 2Micro - >4-2: async1 end
Copy the code

Complete results after all polling is completed

Script start -> async1 start -> error1 -> async2 -> promise1 -> script end micro1-1: promise2 - >1-2: then 1-1Micro - >1-3: promise3 macro2:setTimeout3-1: promise4 - >3-2: undefined4-1: 2Micro - >4-2: async1 end
Copy the code

If you got all four questions right, congratulations.

Write in the last

For the [front-end system] this series of articles I am holding a very serious, very want to write a good state of mind, but after all, I am still front small white & writing new, if the article has that piece of writing is not very good or there are problems welcome to point out, I will be in the back of the article kept modifying. I hope I can grow with you as I progress. Those who like my article can also pay attention to it

I’ll be grateful to the first people to pay attention. At this time, you and I, young, packed lightly; And then, rich you and I, full of it.

series

[Front end system] Build a great oaks from the foundation

[Front-end System] The application scenario of re in development is not just rule verification

Refer to the article

In-depth understanding of JavaScript Event loops

Learn the Event Loop once