Front knowledge

JavaScript engines are single-threaded, and a piece of JS code can only be executed from top to bottom in one thread.

JS has a built-in setTimeout function that specifies how many milliseconds after a function or piece of code should be executed.

doubt

console.log(1)
setTimeout(() = >{
    console.log(2)},1000)
console.log(3)
Copy the code

The output should be 1, 2 after 1 second, and 3 after 1 second. However, the reality is that 1 and 3 are printed first, and 2 is printed after 1 second.

Why is setTimeout() not blocked by the following expression in a single threaded JS, ignoring the running state of setTimeout?

Browser threads

If all the code is executed on the main JS thread, then if the setTimeout delay is very long, the following code will wait for setTimeout to return successfully before continuing to execute, then JS efficiency is very low, so to solve this problem, the browser has several threads to assist the main JS thread to run.

  • GUI rendering thread
  • JS engine thread
  • The setTimeout timer triggers the thread
  • . , etc.

The JS engine thread is the main thread, which is the thread running JS code, and the setTimeout thread is the asynchronous thread.

Task queue

To achieve non-blocking, you need to be asynchronous, and how to do that, you need to have a static task queue that stores the callback function that is returned after asynchronous processing.

Synchronization task

Common tasks queued on the main thread are:

  • The output, such asconsole.log()
  • Variable declarations
  • A synchronous function is a function that does not return immediately when called, but does not return until all the tasks in the function have been completed.

Asynchronous tasks

Tasks performed on asynchronous threads, such as setTimeout, and AJAX are common.

The working principle of

The main thread first executes console.log(1), then detects the setTimeout() function and hands it off to the responding asynchronous thread. The main thread then skips setTimeout() and continues with console.log(3) below. SetTimeout () in the main thread enters the asynchronous thread and starts to execute. After a 1-second delay, the callback function of setTimeout() enters the task queue. After the synchronization task of the main thread is completed, the callback function enters the idle period. The console.log(2) callback is executed after setTimeout() is completed. (There is actually an intermediary in the main thread, asynchronous thread, and task queue called the polling processing thread Event Loop, omitted here for simplicity).

The effect of function execution timing on output results

As can be seen from the above description, due to the asynchronous nature of JS, the function execution timing may be different, the final output results may also be different. Asynchronous functions, in particular, are called at a different time from the actual completion of execution in the main thread. In this process, variables involved in the asynchronous function may have changed, and the output may not meet the expectations of us beginners.

A typical example

let i
for(i=0; i<6; i++){setTimeout(() = >{
  	console.log(i)
  }
            ,0)}Copy the code

SetTimeout () is an asynchronous function. Asynchronous functions do not start out on the main thread. The main thread first executes the let I variable declaration and then executes the for loop. Each for loop executes the asynchronous function setTimeout() once, and this asynchronous function moves into the setTimeout timer trigger thread. After execution in this asynchronous thread, the callback function enters the task queue. There are six loops in total. There are six console.log(I) callback functions in the task queue waiting for the main thread to call. After 6 cycles, the synchronization task for loop ends and there are no more tasks to execute in the main thread. The task queue is asked if it is empty. There are 6 console.log(I) in the main thread and they are executed in order of first in, first out.

exception

for(let i=0; i<6; i++){setTimeout(() = >{
  	console.log(i)
  }  	
    ,0)}Copy the code

Instead of 6, 6, 6, 6, 6, 6, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 5, 0, 1, 2, 4, 5, 0, 1, 2, 3, 5 But the previous I in different stages of the creation of the copy, with the carving of a boat to seek a sword has the same wonderful.

Other methods of printing 0, 1, 2, 3, 4, 5

Parameter passing is implemented with parameter functions

    let i
    function a(i){
      setTimeout(function(){
           console.log(i)
      },0); 
    }
    for (i = 0; i < 6; i++) { 
          a.call(undefined,i);
    }
Copy the code

Write the callback function as an immediate function

let i
for(i=0; i<6; i++){setTimeout((() = >{
    console.log(i)
  })(),0)}Copy the code