JS execution order problem

When I meet the execution order of setTimeout, promise and async, I don’t understand it very well

Js event loop (synchronous and asynchronous)

Javascript is a single-threaded language. Web-worker is proposed in HTML5 (link: Introduction and Use of Web-worker), but the core that javascript is single-threaded remains unchanged. So all javascript versions of “multithreading” are modeled with a single thread. All tasks need to be queued to be executed one by one. After one task is executed, the next task can be executed. However, in the process of task execution, the I/O device is very slow, and the next task must be executed after the result is obtained, resulting in extremely low efficiency. The CPU is idle and ready to perform other tasks. So you can completely disregard the IO device during the execution of the task, suspend the waiting task, execute other tasks first, and then return to execute the previous task after the results come out, which can improve efficiency. All tasks are classified into two types, synchronous and asynchronous

  1. Synchronization task
If a task is queued on the main thread, the next task will be executed only after the previous task is completedCopy the code
  1. Asynchronous tasks
A task that does not enter the main thread but enters the task queue will enter the main thread only after the main thread task is completed and the task queue starts to notify the main thread and request the execution of the taskCopy the code

Event loop:

  • Synchronous and asynchronous tasks go to different execution “places”, synchronous tasks go to the main thread, asynchronous tasks go to the Event Table and register functions
  • When the specified Event completes, the Event Table moves this function to the Event Queue
  • If the tasks in the main thread are empty after execution, the Event Queue will read the corresponding function and enter the main thread for execution
  • This process is repeated over and over again, known as an Event Loop.

Example:

    let data = [];
    $.ajax({
        url:
        data:data,
        success:() = > {
            console.log('Sent successfully! '); }})console.log('End of code execution');
Copy the code

Resolution:

  • Ajax goes to the Event Table and registers the callback function SUCCESS
  • Perform the console. The log ()
  • The Ajax Event completes, and the callback function SUCCESS enters the Event Queue
  • The main thread reads success from the Event Queue and executes it
Js has been doing a job, is to extract tasks from the task queue, put into the main thread to execute.
  • All synchronization tasks are executed on the main thread, forming an execution Context stack.

  • In addition to the main thread, there is a “task queue”. Whenever an asynchronous task has a result, an event is placed in the “task queue”.

  • Once all synchronization tasks in the execution stack are completed, the system reads the task queue to see what events are in it. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing.

  • The main thread repeats step 3 above

setTimeout

  1. SetTimeout The execution time is inconsistent with the set time
    setTimeout(() = > {
        task()
    },3000)

    sleep(10000000)
Copy the code

Resolution:

  • Task () enters the Event Table and registers, and the timer starts
  • Execute sleep, very slowly, very slowly, and the timing continues
  • Task () enters the Event Queue, but the sleep is too slow to execute
  • The sleep is finally finished, and task() is finally executed from the Event Queue to the main thread

After the above process is completed, the setTimeout function adds the task(task()) to the Event Queue after a specified time. Since it is a single-threaded task that needs to be executed one by one, if the previous task takes too long, it can only wait, resulting in a real delay time much longer than 3 seconds.

2. SetTimeout (fn,0) Whether the code can be executed immediately

SetTimeout (fn,0) specifies that a task should be executed at the earliest available free time on the main thread. This means that there are no more seconds to wait until all synchronization tasks on the main thread are completed and the stack is empty. According to HTML standards, the minimum is 4 milliseconds.

Js event loop (macro task micro task)

  • Macro-task: includes the entire code script, setTimeout, and setInterval
  • Micro-task: includes Promise, process. NextTick, etc

SetImmediate specifies the mediate callback function, which is always ranked before setTimeout

Microtask priority: process.nextTick > Promise > MutationObserver

The order of the event loop determines the execution order of the JS code. After entering the overall code (macro task), the first loop begins. Then perform all the microtasks. Then start from the macro task again, find one of the task queues to complete, and then execute all the microtasks.

Example:

    setTimeout(function() {
        console.log('setTimeout');
    })

    new Promise(function(resolve) {
        console.log('promise');
        resolve()
    }).then(function() {
        console.log('then');
    })

    console.log('console');
Copy the code
  • This code enters the main thread as a macro task
  • When a setTimeout is encountered, its callback function is registered and distributed to the macro task Event Queue
  • A Promise is then encountered, the New Promise is immediately executed, and the then function is dispatched to the microtask Event Queue
  • If console.log() is encountered, execute immediately
  • The whole script is executed as the first macro task. What are the microtasks? Find then in the microtask Event Queue, execute
  • The first loop ends and the second loop begins, starting with the macro task Event Queue. The callback function corresponding to – — setTimeout in the macro task Event Queue is found and executed immediately

    Promise.resolve().then(() = > {
        console.log("1");  
        setTimeout(() = > { 
          console.log("2");
        }, 0);
      });
      setTimeout(() = > { 
        console.log("3");
        Promise.resolve().then(() = > {
          console.log("4");
        });
      }, 0);
Copy the code
  • (red) : The JS engine stores the microscopic task Promise into the execution stack and the macro task setTimeout into the “task queue”.
  • (green) : The main thread runs the code in the stack first, enters 1, and stores the green box setTimeout to the “task queue”.
  • (blue) : After the execution stack is cleared, the earliest setTimeout (the one in red box) stored in the “task queue” will be read first, and this timer will be stored in the stack, and the execution begins. The code in this timer is all micro tasks, so it can be executed at once, output 3 and 4 successively
  • (purple) : Repeat step 3 and read the last setTimeout in the “Task Queue” (the one in the green box), printing 2

Async and SeTimeout nested execution order problem

Async and await conversion

So async is actually the syntactic sugar for promise, and we’re going to convert it to a promise, and async will return an implicit promise async function MDN explanation

    async function async1(){
      console.log('async1 start')
      await async2()
      console.log('async1 end')}async function async2(){
      console.log('async2')
    }
    async1();
Copy the code

Can be converted to

     function async1() {
        console.log("async1 start");
        const p = async2();
        return new Promise((resolve) = > {
          resolve();
        }).then(() = > {
          p.then(() = > {
            console.log("async1 end");
          });
        });
      }
      function async2() {
        console.log("async2");
        return new Promise((resolve) = > {
          resolve();
        });
      }
      async1();
Copy the code

I had a problem the other day

    async function async1() {
        console.log("async1 start");
        await async2();
        await setTimeout(function(){
          console.log('111')})console.log("async1 end");
      }
      async function async2() {
        console.log("async2");
      }
      async1();
      
     
Copy the code

The output is // async1 start async2 asynC1 end 111

Can be converted to

    function async1() {
        console.log("async1 start");
        const p = async2();
        return new Promise((resolve) = > {
          resolve();
        }).then(() = > {
          p.then(() = > { 
            new Promise((resolve) = >{
                setTimeout(function(){
                    console.log('111')
                }) 
                resolve();
            }).then(() = > { 
                console.log("async1 end"); })}); }); }function async2() {
        console.log("async2");
        return new Promise((resolve) = > {
          resolve();
        });
      }
      async1();
Copy the code
  • This code enters the main thread as a macro task
  • Console. log(“async1 start”) is encountered; Execute // async1 start
  • When a Promise is encountered, the new Promise is immediately executed, and the then function is dispatched to the microtask Event Queue to get the result // async2
  • The whole script is executed as the first macro task. What are the microtasks? If you find then in the microtask Event Queue, execute and call async2. If you meet a promise, execute immediately and distribute then to the microtask Event Queue.
  • When a Promise is encountered, the new Promise executes immediately, including setTimeout, registers its callback function to the macro task Event Queue, and then to the microtask Event Queue as well
  • When the macro task is finished, check the micro task in the Event Queue and execute // async1 end

_ Execute setTimeout // 111 in macro task Event Queue in a loop after the microtask is completed