1、JS EventLoop 事件循环机制
执行栈:存储函数调用的栈结构,遵循先进后出的原则。
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。
微任务包括 process.nextTick ,promise ,MutationObserver,其中 process.nextTick 为 Node 独有。
宏任务包括 script , setTimeout ,setInterval ,setImmediate ,I/O ,UI rendering。
Event Loop 执行顺序如下所示:
先执行宏任务,宏任务执行完之后会去查看延迟队列里面的任务,如果延迟任务里面的时间过期了就将这个延迟任务放在宏任务,然后会接着执行微任务。
- 首先执行同步代码,这属于宏任务
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码(延迟队列里)需要执行
- 执行所有微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
// script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout
注意:新的浏览器中不是如上打印的,因为 await 变快了
// script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout
2、Node EventLoop 事件循环机制
- Node.js采用V8作为js的解析引擎,而I/O处理方面使用了自己设计的libuv
- libuv是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的API
- 事件循环机制也是它里面的实现
- V8引擎解析JavaScript脚本并调用Node API
- libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎
- V8引擎再将结果返回给用户
libuv
- 同步执行全局的脚本
- 执行所有的微任务,先执行nextTick中的所有的任务,再执行其它微任务
- 开始执行宏任务,共有6个阶段,从第1个阶段开始,会执行每一个阶段所有的宏任务
总的来说:
- 先执行全局的同步任务,
- 执行完同步任务之后开始执行微任务的回调【在微任务队列里,先执行
nextTick队列,然后在执行promise的回调】, - 微任务队列执行完了之后,执行
timer(setTimeout\setInterval)的回调 - 然后执行
IO事件队列(执行除了close事件的callbacks、被timers设定的callbacks、setImmediate()设定的callbacks这些之外的callbacks) - 然后进入到
idel,prepare阶段(但这进node内部使用,可忽略) - 然后进入
poll阶段(在这里会获取新的IO事件,适当的条件下node将堵塞在这里) - 然后进入
check阶段(执行setImmediate()的回调) - 然后进入关闭事件
close callbacks阶段,执行socket.on('close',...)这些callback
联系上面的总结,自己写下这段代码的结果:
console.log(1);
setTimeout(() => {
console.log(2);
process.nextTick(() => {
console.log(3);
});
new Promise((resolve) => {
console.log(4);
resolve();
}).then(() => {
console.log(5);
});
});
new Promise((resolve) => {
console.log(7);
resolve();
}).then(() => {
console.log(8);
});
process.nextTick(() => {
console.log(6);
});
setTimeout(() => {
console.log(9);
process.nextTick(() => {
console.log(10);
});
new Promise((resolve) => {
console.log(11);
resolve();
}).then(() => {
console.log(12);
});
});
1,7,6,8,2,4,9,11,3,10,5,12
console.log(1);
setImmediate(function () {
console.log('immediate');
});
setTimeout(() => {
console.log(2);
process.nextTick(() => {
console.log(3);
});
new Promise((resolve) => {
console.log(4);
resolve();
}).then(() => {
console.log(5);
});
});
process.nextTick(() => {
console.log('nextTick1');
Promise.resolve().then(() => console.log('promise4'));
process.nextTick(() => {
console.log('nextTick2');
Promise.resolve().then(() => console.log('promise5'));
process.nextTick(() => {
console.log('nextTick3')
process.nextTick(() => {
console.log('nextTick4')
})
})
})
})
new Promise((resolve) => {
console.log(7);
resolve();
}).then(() => {
console.log(8);
});
process.nextTick(() => {
console.log(6);
});
setTimeout(() => {
console.log(9);
process.nextTick(() => {
console.log(10);
});
new Promise((resolve) => {
console.log(11);
resolve();
}).then(() => {
console.log(12);
});
});
1、7、nextTick1、6、nextTick2、nextTick3、nextTick4、8、promise4、promise5、2、4、9、11、3、10、5、12、immediate
这些是自己的学习总结,如果有错误的地方请指出,谢谢!