JS single-threaded

Let’s talk about single threading in JS. Single threading means that all tasks need to be queued until the previous task is finished before the next task is executed. If the first task takes a long time, the second will have to wait.

That how to solve this problem, the browser platform, the underlying browser opens the multi-thread to perform some task, the formation of the asynchronous task which we used to say, for example setTimeout timer, asynchronous tasks can well solve the problem of code blocks, but it also results in code execution order not as straightforward as synchronization task, This is the focus of this article.

JS runtime mechanism

The entire script is initially executed as a macro task. In the execution process, the synchronous code is executed directly. After the macro task waits for the time or succeeds, the callback of the method is put into the macro task queue, and the micro task enters the micro task queue.

The macro task of the current main thread has been dequeued. Check and clear the microtask queue. Next, render the browser UI thread, check the Web worker task, and execute it.

Then take out a macro task to execute. And so on…

EventLoop is a cyclic mechanism that continuously polls a number of queues to find an execution model in which tasks need to be performed and executed in sequence.

Macro tasks versus microtasks

Macro tasks can be understood to mean that the code executed on each execution stack is a macro task (including getting an event callback from the event queue and putting it on the execution stack each time).

The browser will render the page after the execution of one macro task and before the execution of the next macro task in order to enable the execution of JS internal macro tasks and DOM operations in an ordered way.

Macro tasks include script(overall code), setTimeout, setInterval, I/O, UI interaction event, and MessageChannel.

Microtasks can be understood as tasks that need to be executed immediately after the current task is completed. That is, after the current task, but before rendering, perform the empty microtask.

So it’s more responsive than macro tasks because there’s no waiting for UI rendering.

Microtasks include promise.then, MutaionObserver, process. NextTick (node.js environment), and so on.

Code sample

console.log('start')
setTimeout(() = > {
  console.log('s1')
  Promise.resolve().then(() = > {
    console.log('p2')})Promise.resolve().then(() = > {
    console.log('p3')})})Promise.resolve().then(() = > {
  console.log('p1')
  setTimeout(() = > {
    console.log('s2')})setTimeout(() = > {
    console.log('s3')})})console.log('end')

Copy the code

The code analysisEventLoop

  1. The first encounterconsole.log('start')And the outputstartGo ahead.
  2. encountersetTimeoutBecause ofsetTimeoutIt’s a macro task. When it’s time, put it in the macro task queue and continue executing.
  3. encounterPromise.thenBecause the current task is a microtask, put it into the microtask queue and continue to execute.
  4. encounterconsole.log('end')And the outputend.
  5. At this point, all tasks in the current main thread have been executed,EventLoopStart to work, find the task that needs to be executed and push it into the execution stack, the microtask takes precedence over the macro task, so firstPromise.resolve().thenPush to the execution stack to continue execution.
  6. encounterconsole.log('p1')And the outputp1Go ahead.
  7. Meet twosetTimeout s2.s3, and put it into the macro task queue for execution.
  8. At this point, all tasks in the current main thread are completed, the microtask queue is checked and cleared, since there is no microtask at this time, and the macro task from step 2 is retrievedsetTimeoutStart execution.
  9. The outputs1“And then againp2p3Two microtasks, put them in the microtask queue.
  10. At this point, all tasks in the current main thread are completed, and the microtask queue is checked and cleared. So successive outputp2p3.
  11. At this point, all the tasks in the current main thread are completed, the microtask queue is checked and cleared, since there are no microtasks at this point, and then the macro task from step 7 is fetchedsetTimeoutStart execution. So successive outputs2s3.
  12. The entire JS code has been completed, the final output sequencestart.end.p1.s1.p2p3.s2.s3.