Javascript is a single-threaded, non-blocking scripting language. Single-threaded means that at any time javascript executes code, there is only one main thread to handle all tasks, and this is implemented through an event loop

Task queue
Function call stack

In the latest standard, they are called task and Jobs respectively. Their execution order is that the micro-task queue takes precedence over the >>> macro task queue, which is also known as the distributor. Their role is to distribute tasks to the appropriate queue

Macro-task includes:

Script setTimeout setInterval setImmediate I/O UI Rendering.

Micro-task includes:

Process. nextTick, Promise, Object.observe(deprecated), MutationObserver(new feature in HTML5)

Some of the APIS, such as Process. nextTick and setImmediate, do not exist in the browser but only in Node.js

Let’s go straight to the example

console.log('golb1');

setTimeout(function() {
    console.log('timeout1');
    process.nextTick(function() {
        console.log('timeout1_nextTick');
    })
    new Promise(function(resolve) {
        console.log('timeout1_promise');
        resolve();
    }).then(function() {
        console.log('timeout1_then')
    })
})

setImmediate(function() {
    console.log('immediate1');
    process.nextTick(function() {
        console.log('immediate1_nextTick');
    })
    new Promise(function(resolve) {
        console.log('immediate1_promise');
        resolve();
    }).then(function() {
        console.log('immediate1_then')
    })
})

process.nextTick(function() {
    console.log('glob1_nextTick');
})
new Promise(function(resolve) {
    console.log('glob1_promise');
    resolve();
}).then(function() {
    console.log('glob1_then')
})

setTimeout(function() {
    console.log('timeout2');
    process.nextTick(function() {
        console.log('timeout2_nextTick');
    })
    new Promise(function(resolve) {
        console.log('timeout2_promise');
        resolve();
    }).then(function() {
        console.log('timeout2_then')
    })
})

process.nextTick(function() {
    console.log('glob2_nextTick');
})
new Promise(function(resolve) {
    console.log('glob2_promise');
    resolve();
}).then(function() {
    console.log('glob2_then')
})

setImmediate(function() {
    console.log('immediate2');
    process.nextTick(function() {
        console.log('immediate2_nextTick');
    })
    new Promise(function(resolve) {
        console.log('immediate2_promise');
        resolve();
    }).then(function() {
        console.log('immediate2_then')})})Copy the code

Step 1: The macro task script is executed first. Global push. Glob1 output.

Second, setTimeout is encountered during execution. SetTimeout acts as a task dispatcher to distribute tasks to the corresponding macro task queue. Note that setTimeout is executed directly on the function call stack, and its first argument, the deferred function, is distributed to the setTimeout queue

setTimeout(function() {
    console.log('timeout1');
    process.nextTick(function() {
        console.log('timeout1_nextTick');
    })
    new Promise(function(resolve) {
        console.log('timeout1_promise');
        resolve();
    }).then(function() {
        console.log('timeout1_then')})})Copy the code

Step 3: Execution encounters setImmediate. SetImmediate is also a macro task dispenser that dispatches tasks to the corresponding task queue. SetImmediate’s task queue executes after the setTimeout queue.

setImmediate(function() {
    console.log('immediate1');
    process.nextTick(function() {
        console.log('immediate1_nextTick');
    })
    new Promise(function(resolve) {
        console.log('immediate1_promise');
        resolve();
    }).then(function() {
        console.log('immediate1_then')})})Copy the code

Step 4: Execute when nextTick is encountered, process.nextTick is a microtask dispatcher that dispatches tasks to the corresponding microtask queue.

process.nextTick(function() {
    console.log('glob1_nextTick');
})
Copy the code

Step 5: Implement meet Promise. Promise’s then method distributes the task to the corresponding microtask queue, but the methods in its constructor execute directly. Therefore, glob1_PROMISE will output a second output. If a Promise has a setTimeout in its construction option, it will be executed immediately, but its argument 1 (deferred execution) will be pushed into the setTimeout queue

new Promise(function(resolve) {
    console.log('glob1_promise');
    resolve();
}).then(function() {
    console.log('glob1_then')})Copy the code

Step 6: Execution encounters a second setTimeout. It will come after the first setTimeout

setTimeout(function() {
    console.log('timeout2');
    process.nextTick(function() {
        console.log('timeout2_nextTick');
    })
    new Promise(function(resolve) {
        console.log('timeout2_promise');
        resolve();
    }).then(function() {
        console.log('timeout2_then')})})Copy the code

Step 7: When nextTick and Promise are encountered, process.nextTick willglob2_nextTickPush into the corresponding queue, and then the new Promise willconsole.log('glob2_promise')Enter the function call stack and execute the exit, promise.then push into the Promise microtask queue

process.nextTick(function() {
    console.log('glob2_nextTick');
})
new Promise(function(resolve) {
    console.log('glob2_promise');
    resolve();
}).then(function() {
    console.log('glob2_then')})Copy the code

Step 8: Encounter setImmediate again

setImmediate(function() {
    console.log('immediate2');
    process.nextTick(function() {
        console.log('immediate2_nextTick');
    })
    new Promise(function(resolve) {
        console.log('immediate2_promise');
        resolve();
    }).then(function() {
        console.log('immediate2_then')})})Copy the code

The nextTick queue is executed before Promie. After the executables in nextTick are complete, the tasks in the Promise queue will be executed.

It is important to note that when the current microtask queue is empty, it is called the end of an event loop

At this point, the second cycle starts, and a macro task is listed from setTimeout. It will repeat the above process and distribute the task to the corresponding queue. If its setTimeout has a nested setTimeout, it will be pushed to the bottom of the setTimeout queue. It will be executed later in the loop.

Async and await

The execution order of async and await in the task queue

Async is used to define an asynchronous function. Its asynchracy is determined by the await of the function. An await will return the following expression (either a promise object or a constant) as a promise object

The statement below await XXX is equivalent to XXX. Then (resolve)

  1. If XXX is a promise and the return value is a Thenable object (resove(),reject()), then the state of the awati promise will be determined by it. If reslove is fulfilled, that will be fulfilled. Then XXX. Then (resolve) will be executed, whereas the statement after awati will not be executed
  2. If XXX returns a null or a constant, such as console, await will process it with promise. Resolve (value) into a fulfilled promise object, which will be fulfilled later. And pass the value to the statement after await XXX, which is xxx.then(resolve(value))

Let’s do another example

async function async1() {
    console.log( 'async1 start' )
    await async2()
    console.log( 'async1 end')}async function async2() {
    console.log( 'async2' )
}
async1()
console.log( 'script start' )

// Output the result
async1 start
async2
script start
async1 end
Copy the code
  1. First of all, async functions are called in the same way as synchronous functions, directly print async1 start

  2. Then we get await async2()

Promise.resolve(asyncs()).then(console.log('async1 end')
Copy the code

At this time async2 will be called directly, because it is equivalent to the construction option in new Promise. Its return value is async2, which is encapsulated with await as a fulfilled promise. And its value will be transmitted to THEN, which will be pressed into the promise

  1. Execute the script start

  2. Execute async1 end in the promise.then queue

To sum up:

  1. Microtask queues take precedence over macro task queues
  2. The process.nextTick queue has the highest priority among microtask queues
  3. The promise’s construction options are executed directly
  4. Promise. then will be pushed into the promise queue
  5. SetTimeout is executed directly, and its delay function is pushed into the setTimeout queue
  6. Setinterval and setTimeout belong to the same queue
  7. An event loop ends when all microtask queues are empty