单线程和异步
- JS是单线程的(无论在浏览器还是nodejs)
- 浏览器中JS执行和DOM渲染同一个线程
- 异步
宏任务和微任务
- 宏任务,如setTimeout setInterval 网络请求
- 微任务,如promise async/await
- 微任务在下一轮DOM渲染之前执行,宏任务在之后执行
代码演示
console.log('start');
setTimeout(()=>{
console.log('timeout');
})
Promise.resolve().then(()=>{
console.log('promise then');
})
console.log('end');
- 先start 后end 执行同步任务
- 后微任务Promise
- 后宏任务setTimeout
演示代码
<body>
<p>event-loop</p>
<script>
const p = document.createElement('p')
p.innerHTML = 'new paragraph'
document.body.appendChild(p)
const list = document.getElementsByTagName('p')
console.log('length----', list.length)
console.log('start')
// 渲染之后
setTimeout(() => {
const list = document.getElementsByTagName('p')
console.log('length on timeout', list.length)
alert('阻塞 timeout')
})
// 渲染之前
Promise.resolve().then(() => {
const list = document.getElementsByTagName('p')
console.log('length on promise.then ----', list.length)
alert('阻塞 promise')
})
console.log('end')
</script>
</body>
结果:
点击确定后👇
- 宏任务和微任务都不会干扰JS对DOM的操作!
浏览器 event loop
代码演示
console.log('start')
setTimeout(() => {
console.log('timeout')
})
Promise.resolve().then(() => {
console.log('promise then')
})
console.log('end')
// 先执行完同步代码,遇到一些异步的API或者函数分别放到宏任务和微任务队列里(不一定立马就放,如ajax或者setTimeout有时间间隔,会到触发点后再放,同步任务执行完后执行微任务、然后宏任务(中间是DOM渲染) 然后 event loop 继续监听
// 然后👇
// 宏任务队列 MarcoTask Queue 队列
// () => {
// console.log('timeout')
// }
// 微任务队列 MicroTask Queue 队列 把微任务队列里的函数执行
// () => {
// console.log('promise then')
// }
//执行完后继续监听有没有新的任务 进入宏任务/微任务队列
先执行完同步代码,遇到一些异步的API或者函数分别放到宏任务和微任务队列里(不一定立马就放,如ajax或者setTimeout有时间间隔,会到触发点后再放,同步任务执行完后执行微任务、然后宏任务(中间是DOM渲染) 然后 event loop 继续监听
Nodejs 异步
- Nodejs 同样使用ES语法,也是单线程,也需要异步
- 异步也分为:宏任务 + 微任务
- 但是,它的宏任务和微任务,分不同类型,有不同优先级
代码演示
console.info('start');
setImmediate(() => {
console.info('setImmediate');
})
setTimeout(() => {
console.info('timeout');
})
Promise.resolve().then(() => {
console.info('promise then');
})
process.nextTick(() => {
console.info('nextTick');
})
console.info('end')
结果
nodejs 宏任务类型和优先级
- Timers - setTimeout setInterval
- I/O callbacks - 处理网络、流、TCP的错误回调
- Idle, prepare - 闲置状态(nodejs 内部使用)
- Poll 轮询 - 执行poll 中的I/O 队列
- Check 检查 - 存储 setImmediate 回调
- close Callback - 关闭回调,如socket.on('close')
nodejs 微任务类型和优先级
- 包括:promise,async/await,process.nextTick
- 注意, process.nextTick 优先级最高
nodejs event loop
- 执行同步代码
- 执行微任务(process.nextTick 优先级更高)
- 按顺序执行6个类型的宏任务(每个任务开始之前都执行当前的微任务)
答案
- 浏览器和nodejs的event loop 流程基本相同
- nodejs宏任务和微任务分类型,有优先级
- 推荐使用setImmediate代替process.nextTick
- 本文基于nodejs 16版本。nodejs低版本可能会有不同