JavaScript is a single-threaded language. Single-threaded means that only one task can be completed at a time. If there are multiple tasks, they must be queued, the first one completed, the next one executed, and so on. The advantage of this mode is that it is relatively simple to implement and the execution environment is relatively simple. The disadvantage is that as long as one task takes a long time, subsequent tasks must wait in line, which will delay the execution of the whole program. A common form of browser unresponsiveness, known as suspended animation, is when a piece of Javascript code runs for a long time, such as an infinite loop, causing the page to get stuck in one place and unable to perform other tasks

Enforcement mechanism

To solve this problem, Javascript separates the execution modes of tasks into two modes: Synchronous and Asynchronous, Synchronous or Asynchronous, indicating whether the process needs to be completed sequentially, and blocking or non-blocking, indicating whether the function you call tells you the result immediately

  • Synchronization in synchronization mode, the latter task waits for the end of the previous task and then executes it. The execution sequence of the program is synchronized with the order of the tasks

    let i = 100;
    while(--i) {console.log(i); }console.log("I can't execute until the while is done.");
    Copy the code
  • Asynchronous asynchronous execution is blocking mode, each task has one or more of the callback, the callback function after a task before, not to perform a task, after but the callback function, after a task is before the end of a task before execution, so the program execution order the order is not consistent with the task, asynchronous. The browser allocates only one Js thread for each Tab. The main task is to interact with the user and manipulate the DOM, etc. This determines that it can only be a single thread, otherwise it will bring very complex synchronization problems. For example, if there are two JavaScript threads at the same time, one thread adds content on a DOM node. Another thread removes the node, and the browser cannot determine which thread is responsible

    setTimeout(() = > {console.log("I'll execute later.")},0);
    // Note that the W3C standard requires setTimeout to be set to 4ms if the interval is less than 4ms. This also depends on browser Settings, main threads, and task queues. The execution time may be greater than 4ms
    For example, older browsers set the minimum interval to 10 milliseconds. In addition, DOM changes, especially those involving page re-rendering, are usually not made immediately, but every 16 milliseconds
    console.log("I'll execute first.");
    Copy the code

Asynchronous mechanisms


setTimeout( ()  => console.log("I did it after a long time."), 0);
let i = 3000000;
while(--i) { }
console.log("Loop test completed.")
Copy the code

JavaScript asynchrony is realized through an execution stack and a task queue to complete asynchronous operations, all synchronous tasks are executed on the main thread, forming the execution stack; The task queue holds various event callbacks (also known as messages), and when the execution stack completes, the main thread reads the tasks in the task queue and executes them, repeating the loop. For example, the event callback after the completion of setTimeout in the above example exists in the task queue. It should be noted here that the browser timing counter is not counted by the JavaScript engine, because the JavaScript engine is single-threaded. If the thread is blocked, the accuracy of timing will be affected. The count is done by the browser thread. When the count is complete, the event callback is added to the task queue. HTTP requests also have a separate thread in the browser, and the event callback is put into the task queue after completion. It is because the main thread, i.e. the code in the execution stack, has not completed and will not read the event callback in the task queue to execute it, even though the event callback is already in the task queue

  • Event Loop The main thread reads events from the task queue in a continuous Loop, so the whole operation mechanism is calledEvent Loop.Event LoopIs an execution model with different implementations in different places. Browsers and NodeJS implement their own Event loops based on different technologies. The Event Loop of the browser is clearly defined in the SPECIFICATION of HTML5. The Event Loop of NodeJS is implemented in the browser based on LibuvEvent LoopBy the execution stackExecution StackBackground threadBackground Threads, macro queueMacrotask QueueMicro queue,Microtask Queuecomposition
  • An execution stack is a data structure that performs synchronization tasks on the main thread. Function calls form a stack of several frames
  • Background threads are the browser implementation forsetTimeout.setInterval.XMLHttpRequestAnd so on
  • Macro queue, the callback of some asynchronous tasks will enter the macro queue in turn, waiting to be called later, includingsetTimeout,setInterval,setImmediate(Node),requestAnimationFrame,UI rendering,I/OOperations such as
  • Microqueue, the callbacks of other asynchronous tasks will in turn enter the microqueue, waiting for subsequent calls, includingPromise,process.nextTick(Node),Object.observe,MutationObserverOperations such as

When JavaScript executes, the following flow occurs

  • First, the code in the stack is executed synchronously, and the asynchronous tasks in the code are added to the background thread
  • After the synchronous code in the execution stack completes execution, the execution stack clears and the microqueue is scanned
  • Take out the first task in the queue and put it into the execution stack. At this time, the queue will execute the queue operation
  • When the execution stack is completed, the micro-queue tasks continue to be queued and executed until all the micro-queue tasks are executed
  • The task in the microqueue is empty after the last microqueue task is out of the queue and entered into the execution stack. When the execution stack task is completed, the microqueue is empty and the macro queue task continues to be scanned. The macro queue is out of the queue and put into the execution stack
  • Repeat the above steps
// Step 1

// Step 2
setTimeout( () = > {
  Promise.resolve().then( () = > {
    console.log(3); })},0);

// Step 3
new Promise( (resolve, reject) = > {
}).then( () = > {

// Step 4
setTimeout( () = > {
}, 0);

// Step 5
Copy the code

Output: 1, 4, 7, 5, 2, 3, 6