1
头图
To understand asynchrony and synchronization in JS, you need to first understand the execution process of JS code and Event Loop.

Execution of JavaScript code

The operations that the program needs to perform will be put into the Call Stack (A LIFO (Last In, First Out) Stack), a first-in, last-out data structure.

 const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  bar()
  baz()
}

foo()

When this code executes, foo() will be called first. Inside foo(), bar() is called first, then baz() is called. At this moment the Call Stack looks like this:
图例

Each step of the operation is performed, and a new operation is pushed onto the top of the stack. After the execution is completed, it is popped from the top of the stack until the entire stack becomes Empty.

The function of the Event Loop is to go back each iteration to check whether there is an instruction to be executed in the Call Stack, and then execute it.

Asynchronous, synchronous in JavaScript

Execute code synchronously

In most cases, JavaScript executes code synchronously:

 let log = console.log;

let a = 5;
let b = 50;

let a1 = function() { return 5 }
let b1 = function() { return 50 }

log(a1())
log(a2())

let a2 = function(num) { return 5*num }
let b2 = function() { return 50 }

log(a2(b2))
// 打印出:
// 5
// 50
// 250

code that executes asynchronously

setTimeout, callbacks, Promise, fetch, ajax, filesystem interaction, database calls, DOM event listener

In the above cases the code will be executed asynchronously.

The reason is that when the code executes these methods, it is uncertain how long the corresponding operation can be executed, so it will continue to execute downward.

Consider the following situation:

 let a3 = 100;
setTimeout(function() { a3++ }, 0);
log(a3)
setTimeout(function() { log(a3) }, 0);
// 打印出
// 100
// 101

Synchronous code is put into the Call Stack for execution, and asynchronous code is put into a queue (Message Queue). The Event Loop will prioritize the tasks in the Call Stack. When the Call Stack is empty, it will take out the tasks in the Message Queue for execution.

ES6 Job Queue

ECMAScript 2015 introduced the concept of Job Queue, which is used by Promises. It makes the result of the asynchronous method execute as soon as possible, rather than at the end of the Call Stack.

Promises are a very nice implementation of async:

 const bar = () => console.log('bar')

const baz = () => console.log('baz')

const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  new Promise((resolve, reject) =>
    resolve('should be right after baz, before bar')
  ).then(resolve => console.log(resolve))
  baz()
}

foo()

// foo
// baz
// should be right after baz, before bar
// bar

You can see that Promise will execute before setTimeout.

Take a look at this code, what is the execution order of it?

 console.log(1);

setTimeout(() => console.log(2));

Promise.resolve().then(() => console.log(3));

Promise.resolve().then(() => setTimeout(() => console.log(4)));

Promise.resolve().then(() => console.log(5));

setTimeout(() => console.log(6));

console.log(7);
// 1
// 7
// 3
// 5
// 2
// 6
// 4

Nodejs Process.NextTick() and SetImmediate()

Process.NextTick() will be executed at the end of a cycle of the event loop. This method can be implemented to execute an asynchronous method as soon as possible instead of placing it in the asynchronous queue.

The Process.NextTick callback function will be added to the Process.NextTick queue, Promise.Then() will be added to the Promises Microtask Queue, and SetTimeout, SetImmediate will be added to the Macrotask Queue.

SetTimeout() The asynchronous delay of 0ms is very similar to SetImmediate(), they are both executed in the next event loop.

The event loop executes the Process.NextTick queue first, then the Promises microtask queue, then the macrotask queue.

 console.log('script start');

// 异步
Promise.resolve().then(function() {
  console.log('promise');
}).then(function() {
  console.log('promise-then');
});

// 异步
setImmediate(function() {
    console.log('setImmediate')
})

// 异步
setTimeout(function() {
    console.log('setTimeout 0')
}, 0)

// 异步
setTimeout(function() {
    return new Promise(resolve => {
        console.log('setTimeout-delay 100ms promise')
        resolve()
    }).then(res => {
        console.log('setTimeout-delay 100ms promise.then')
    })
}, 100)

process.nextTick(function() {
    console.log('process.nextTick')
})

console.log('script end');

/*
script start
script end
process.nextTick
promise
promise-then
setTimeout 0
setImmediate
setTimeout-delay 100ms promise
setTimeout-delay 100ms promise.then
*/

Reference link

https://nodejs.dev/en/learn/the-nodejs-event-loop/

The article was first published on IICCOM-Personal Blog | Technical Blog "Asynchronous and Synchronous in JavaScript"


来了老弟
508 声望31 粉丝

纸上得来终觉浅,绝知此事要躬行