事件循环

176 阅读2分钟

1. 浏览器的事件循环

  • 宏任务队列(macrotask queue):ajax、setTimeout、setInterval、DOM监听、UI Rendering等
  • 微任务队列(microtask queue):Promise的then回调、 Mutation Observer API、queueMicrotask()等
浏览器事件循环的过程
    1. 从上至下执行所有的同步代码(编写的顶层script代码);
    1. 遇到宏任务,微任务将连个分别加到各自的事件队列中;
    1. 同步代码执行完成后,优先执行==微任务队列的任务==。然后在执行宏任务;
    1. 在执行任何一个宏任务之前(不是队列,是一个宏任务),都会先查看微任务队列中是否有任务需要执行
    • 也就是宏任务执行之前,必须保证微任务队列是空的(都会去清空微任务队列);
    • 如果不为空,那么就优先执行微任务队列中的任务(回调);

2. node.js的事件循环

  • nodejs宏任务事件队列如下
  1. 定时器(Timers):本阶段执行已经被 setTimeout()setInterval() 的调度回调函数。
  2. 待定回调(Pending Callback):对某些系统操作(如TCP错误类型)执行回调,比如TCP连接时接收到ECONNREFUSED。
  3. idle, prepare:仅系统内部使用。
  4. 轮询(Poll):检索新的 I/O 事件;执行与 I/O 相关的回调;
  5. 检测:setImmediate() 回调函数在这里执行。
  6. 关闭的回调函数:一些关闭的回调函数,如:socket.on('close', ...)
  • 微任务(不是队列)(microtask queue):Promise的then回调、 process.nextTick()(优先级高于promise.then的回调)
nodejs完整事件环:
  1. 执行同步代码,将不同任务添加至相应的队列。
  2. 所有同步代码执行后会去执行满足条件的微任务
  3. 所有微任务代码执行完成后会执行timer队列中满足的宏任务(新版node,改为和浏览器一致,执行完一个宏任务就回去清空微任务
  4. timer中所有的宏任务执行完成之后就会依次切换队列(按照上面的顺序)。在完成队列切换之前会先清空微任务

常见的问题

//第一个问题:
setTimeout(()=>{
	console.log("timeoout")
},0);
setImmediate(()=>{
    console.log("immediate")
});

//多次输出出现不同结果,原因是因为:setTimeout的延迟不准
//1.第一次
timeoout
immediate
//2.第n次
immediate
timeoout


//第二个问题
const fs = require('fs')
fs.readFile("./m.js",()=>{
    setTimeout(()=>{
        console.log("timeoout")
    },0);
    setImmediate(()=>{
        console.log("immediate")
    });
})
//输出结果都是如下,原因:fs.readFile是在Poll队列中执行,而Poll队列执行完成之后就会去执行check队列,然后在重新执行事件循环执行timer中的任务。
immediate
timeoout