1. 浏览器的事件循环
- 宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
- 微任务队列(microtask queue):Promise的then回调、 Mutation Observer API、queueMicrotask()等
浏览器事件循环的过程
-
- 从上至下执行所有的同步代码(编写的顶层script代码);
-
- 遇到宏任务,微任务将连个分别加到各自的事件队列中;
-
- 同步代码执行完成后,优先执行==微任务队列的任务==。然后在执行宏任务;
-
- 在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
-
- 也就是宏任务执行之前,必须保证微任务队列是空的(都会去清空微任务队列);
- 如果不为空,那么就优先执行微任务队列中的任务(回调);
2. node.js的事件循环
- 定时器(Timers):本阶段执行已经被
setTimeout() 和 setInterval() 的调度回调函数。
- 待定回调(Pending Callback):对某些系统操作(如TCP错误类型)执行回调,比如TCP连接时接收到ECONNREFUSED。
- idle, prepare:仅系统内部使用。
- 轮询(Poll):检索新的 I/O 事件;执行与 I/O 相关的回调;
- 检测:
setImmediate() 回调函数在这里执行。
- 关闭的回调函数:一些关闭的回调函数,如:
socket.on('close', ...)。
- 微任务(不是队列)(microtask queue):Promise的then回调、 process.nextTick()(优先级高于promise.then的回调)
nodejs完整事件环:
- 执行同步代码,将不同任务添加至相应的队列。
- 所有同步代码执行后会去执行满足条件的微任务
- 所有微任务代码执行完成后会执行timer队列中满足的宏任务(新版node,改为和浏览器一致,执行完一个宏任务就回去清空微任务)
- timer中所有的宏任务执行完成之后就会依次切换队列(按照上面的顺序)。在完成队列切换之前会先清空微任务。
常见的问题
setTimeout(()=>{
console.log("timeoout")
},0);
setImmediate(()=>{
console.log("immediate")
});
timeoout
immediate
immediate
timeoout
const fs = require('fs')
fs.readFile("./m.js",()=>{
setTimeout(()=>{
console.log("timeoout")
},0);
setImmediate(()=>{
console.log("immediate")
});
})
immediate
timeoout