调用栈、消息队列(任务队列、eventloop)
调用栈 call stack:先进后出
function A() {
setTimeout(() => {
console.log(new Date().getTime());
}, 0);
B();
}
function B() {
console.log('b')
}
A()
上述例子中,call stack首先是A函数进入([A]),发现A函数中调用B函数,那么call stack.unshift(B),现在就是[B, A],等待B执行完,就会shift,把B弹出,垃圾回收掉,A执行完,再shift,把A弹出,再次垃圾回收机制回收掉。
消息队列(任务队列、eventloop):分为 微任务队列、宏任务队列
微任务队列
1. promise 回调
2. process.nextTick,优先级高于 promise
3. MutationObserver,DOM变化的时候(浏览器环境,非 node 环境)
宏任务队列
1. timer(setTimeout, setInterval)
2. setImmediate,优先级高于 setTimeout
3. I/O 事件,fs.readFile()
4. UI 渲染
这里有个事件循环可以注意一下的点(因为我经常会忘了)
事件循环时,先执行完同步代码,再执行异步代码,其中异步代码,会先执行完所有微任务,然后异步消息队列去宏任
务队列拉取`一个宏任务`执行,若其中有微任务产生,那就再把所有的微任务执行完,再去宏任务队列拉取一个宏任务
执行,以此循环。
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end');
}
// 本质上等同于上面async1执行时
// new Promise((resolve) => {
// console.log('async1 start')
// async2();
// resolve();
// }).then(() => {
// console.log('async1 end')
// })
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout0')
}, 0)
setTimeout(function () {
console.log('setTimeout2')
}, 300)
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick1'));
async1();
process.nextTick(() => console.log('nextTick2'));
new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
})
console.log('script end')
上述代码就是面试常考的node的事件循环
其结果为:
// 首先是同步代码
console.log('script start')
console.log('async1 start')
console.log('async2') // 在这里,因为await将其异步变为同步代码,所以在调用async2函数后的console.log('async1 end');已变为微任务,放入微任务队列。
console.log('promise1') // new Promise首个函数其实是同步代码
console.log('promise2')
console.log('script end')
// 异步消息队列
// 微任务
console.log('nextTick1'))
console.log('nextTick2'))
console.log('async1 end');
console.log('promise3')
// 宏任务
console.log('setTimeout0') //但是这里其实是与上述宏任务中说的第2. setImmediate,优先级高于 setTimeout;是有冲突的,具体原因看下方描述
console.log('setImmediate')
console.log('setTimeout2')
// 这样的输出结果就很符合逻辑的 setImmediate 优先级高于 setTimeout 了
// 先输出setImmediate
setTimeout(() => {
console.log("setTimeout");
}, 0);
setImmediate(() => {
console.log("setImmediate");
});
我这里是这样理解的,node环境中,默认情况下, setTimeout 的延迟时间是1毫秒,即使将延迟设置为0毫秒,实际上也会有1毫秒的延迟。
根据stackoverflow.com中的解释,setTimeout 的延迟参数是指执行回调函数的最小等待时间,而不是确切的时间。
所以如果前面没有别的代码,只有 setTimeout、setImmediate的话,在同一时间执行时,setImmediate是快一步的;
但是如果前面有别的代码,那么其实在执行到settimeout的时候,已经过去了1毫秒了,所以才会直接就输出settimeout。