Permanent thread

The browser’s resident threads are:

  1. Js engine threads (interpret executing JS, user input, network requests)
  2. GUI thread (draws the user interface and is mutually exclusive with the JS main thread)
  3. HTTP network request thread (processing users’ GET, POST and other requests, etc., and pushing the callback function to the task queue after the result is returned)
  4. Timing trigger thread (setTimeout, setInterval after waiting time to push the execution function into the task queue)
  5. Browser event processing thread (putting click, Mouse, and other interactive events into an event queue after they occur)

Most browsers have these five threads, which are coordinated through the UI main thread.

Js engine threads and GUI threads are mutually exclusive: JS can manipulate DOM elements and affect GUI rendering results, so JS engine threads and GUI rendering threads are mutually exclusive. This means that while the js engine thread is running, the GUI rendering thread will be frozen.

Js single-threaded

JavaScript is run on a single thread and can be executed asynchronously. In general, such single-threaded and asynchronous languages are event-driven, and browsers provide such an environment for JavaScript.

The principle expressed in the figure above:

  1. Synchronous and asynchronous tasks go to different execution “places”, synchronous tasks go to the main thread (execution stack) and asynchronous tasks go to the Event Table and register functions
  2. The Event Table moves this function to the Event Queue when the specified task is completed.
  3. After the tasks in the main thread are executed, 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 Event Loop. Event polling is closely related to macro and micro tasks.

The timer

SetTimeout is not executed directly after the waiting time expires, but enters a task queue in the browser first. The tasks in the task queue are invoked after the synchronization queue ends. SetTimeout (function(){},0) : setTimeout(function(){},0) : setTimeout(function(){},0) : setTimeout(function(){},0) : setTimeout(function(){},0) : setTimeout(function(){},0) : setTimeout(function(){},0) : setTimeout(function(){},0) But 0ms is actually unattainable, and according to THE HTML standard, the minimum is 4ms. In addition, if the main js thread execution time is too long, it will also wait for the main thread to complete, so the wait time of this function is more than 0ms. SetInterval is to place tasks in an Event Queue at intervals, using the same execution mechanism as setTimeout.

Macro tasks & micro tasks

In an event loop, asynchronous events return results that are placed in a task queue. However, depending on the type of asynchronous event, the event can actually be queued to the corresponding macro or microtask queue. And when the current stack is empty, the main thread checks for events in the microtask queue. If not, fetch an event from the macro task queue and add the corresponding event back to the current stack. If so, the queue will execute the corresponding callback until the microtask queue is empty, then fetch the first event from the macro task queue, and add the corresponding callback to the current stack. And so on and so on and so on.

Macro task

Common macro tasks: I/O, setTimeout, setInterval, setImmediate, requestAnimationFrame

Micro tasks

Common micro tasks: process. NextTick (), MutationObserver, Promise. Then/catch/finally

[Task 1] Add macro tasks and micro tasks to the main thread

console.log('-------start--------');
setTimeout(() = > {
    console.log('setTimeout');
}, 0);

new Promise((resolve, reject) = > {
    for (let i = 0; i < 5; i++) {
        console.log(i);
    }
    resolve()
}).then(() = > {
    console.log('Promise instance callback executes successfully ')})console.log('-------end--------');
Copy the code

Order of execution:

  1. Execute the main thread firstconsole.log('-------start--------')And then the nextsetTimeoutTo execute the macro task in the next loop
  2. Then execute the main threadPromiseThe code is printed separately0, 1, 2, 3, 4And then the next.thenIs a microtask, which is executed immediately after the macro task in this loop completes
  3. Then continue to execute the last of the main threadconsole.log('-------end--------')
  4. Perform microtasksPromise.thenTo printConsole. log('Promise instance callback successfully executed ')
  5. Executing macro taskssetTimeoutTo printconsole.log('setTimeout')

Output result:

-- -- -- -- -- -- -- start -- -- -- -- -- -- -- - 0 1 2 3 4 -- -- -- -- -- -- -- end -- -- -- -- -- -- -- -- Promise instance setTimeout success callback executionCopy the code

[Task 2] Create a micro-task in a micro-task

setTimeout(() = > console.log(4))
new Promise(resolve= > {
    resolve()
    console.log(1)
}).then(() = > {
    console.log(3)
    Promise.resolve().then(() = > {
        console.log('before timeout')
    }).then(() = > {
        Promise.resolve().then(() = > {
            console.log('also before timeout')})})})console.log(2)
Copy the code

Order of execution:

  1. setTimeoutIt’s a macro task, put it in the next loop
  2. performPromiseIn the code, printconsole.log(1)
  3. Continue to execute the main JS code and printconsole.log(2)
  4. Enter thePromise.then()Microtasks, printingconsole.log(3)Continue to execute the containedPromiseTo printconsole.log('before timeout')And continue to perform its microtasks.then()To printconsole.log('also before timeout')
  5. Proceed to the next loop, executesetTimeoutMacro task, printconsole.log(4)

Output result:

1
2
3
before timeout
also before timeout
4
Copy the code

[Task 3] Create a micro task in a macro task

// setTimeout-A
setTimeout(() = > {
    console.log('timer_1');
    // setTimeout-C
    setTimeout(() = > {
        console.log('timer_3')},0)
    new Promise(resolve= > {
        resolve()
        console.log('new promise')
    }).then(() = > { 
        console.log('promise then')})},0)
// setTimeout-B
setTimeout(() = > { 
    console.log('timer_2')},0)
console.log('========== Sync queue ==========')
Copy the code

Order of execution:

  1. I’m going to execute the main thread JS, the first fewsetTimeoutPut it in the next cycle,console.log('========== Sync queue ==========')executed
  2. performsetTimeout-ATo printconsole.log('timer_1'), new Promise code, printconsole.log('new promise'), proceed to execute its microtask code, printconsole.log('promise then')
  3. performsetTimeout-BTo printconsole.log('timer_2')
  4. performsetTimeout-CTo printconsole.log('timer_3')

Output result:

========== Sync queue ==========
timer_1
new promise
promise then
timer_2
timer_3
Copy the code

[Task 4] Create a macro task in a micro task

new Promise((resolve) = > {
    console.log('new Promise(macro task 1)');
    resolve();
}).then(() = > {
    // Microtask 1
    console.log('micro task 1');
    setTimeout(() = > { // Macro task 3
        console.log('macro task 3');
    }, 0)})setTimeout(() = > { // Macro task 2
    console.log('macro task 2');
}, 1000)
console.log('========== Sync queue(macro task 1) =========='); 
Copy the code

Order of execution:

  1. Execute the main thread js firstnew PromiseThe code inconsole.log('new Promise(macro task 1)')
  2. Continue to execute the main thread, encounteredsetTimeoutMove to the next loop and continueconsole.log('========== Sync queue(macro task 1) ==========')
  3. Perform microtasksPromise.then()To printconsole.log('micro task 1')
  4. Continue to execute the macro task under the microtasksetTimeoutTo printconsole.log('macro task 3')
  5. Execute the main threadsetTimeoutTo printconsole.log('macro task 2')

Output result:

new Promise(macro task 1)
========== Sync queue(macro task 1) ==========
micro task 1
macro task 3
macro task 2
Copy the code

[Task 5] Synthesis

console.log('开始');
new Promise(resolve= > {
    console.log('Console. log for Promise1 in main thread');
    resolve();
}).then(() = > {
    console.log('console.log for promise1.then () in main thread');
    setTimeout(() = > {
        console.log('setTimeout for promise.then () in the main thread');
    }, 0);
})
console.log('console.log in main thread');
setTimeout(() = > {
    console.log('Console. log1 for setTimeout in main thread');
    new Promise(resolve= > {
        console.log('Promise of setTimeout in the main thread');
        resolve();
    }).then(() = > {
        console.log('setTimeout promise.then () in the main thread');
    })
    console.log('Console. log2 for setTimeout in main thread');
    setTimeout(() = > {
        console.log('setTimeout of setTimeout in the main thread');
    }, 0);
}, 0);
new Promise(resolve= > {
    console.log('Console. log for Promise2 in main thread');
    resolve();
}).then(() = > {
    console.log('console.log for promise2.then () in main thread');
})
console.log('the end');
Copy the code

Output result:

Log of the Promise1 main thread console.log of the Promise2 main thread Ends console.log of the promise1.then () main thread Console. log of setTimeout console.log1 of setTimeout Promise of setTimeout console.log2 of setTimeout SetTimeout setTimeout of setTimeout in the main thread promise.then () setTimeout of promise.then () setTimeout of setTimeout in the main threadCopy the code