Event Loop总结
什么是Event Loop
JavaScript 是单线程的,有了 event loop 的加持,Node.js 才可以非阻塞地执行 I/O 操作,把这些操作尽量转移给操作系统来执行。
当某个操作结束了,操作系统就会通知 Node.js,然后 Node.js 就(可能)会把对应的回调函数添加到 poll(轮询)队列,最终这些回调函数会被执行。
Event Loop阶段
浏览器
任务被分为Task(又称为MacroTask,宏任务)和MicroTask(微任务)两种。它们分别包含以下内容:
MacroTask: script(整体代码), setTimeout, setInterval, setImmediate(node独有), I/O, UI rendering
MicroTask: process.nextTick(node独有), Promises, Object.observe(废弃), MutationObserver
总的执行顺序为同步代码—>microTask—>macroTask
每一个任务队列里的任务是严格按照先进先出的顺序执行的。但是,因为浏览器自己调度的关系,不同任务队列的任务的执行顺序是不确定的。
具体来说,浏览器会不断从task队列中按顺序取task执行,每执行完一个task都会检查microtask队列是否为空(执行完一个task的具体标志是函数执行栈为空),如果不为空则会一次性执行完所有microtask。然后再进入下一个循环去task队列中取下一个task执行,以此类推。
遇到async await,可以将代码改写成promise.then()的形式。await后面的代码全部都放到then中执行。
Node
nodejs的Event Loop分为6个阶段,它们会按照顺序反复运行,分别如下:
- timers:执行setTimeout() 和 setInterval()中到期的callback。
- I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行
- idle, prepare:队列的移动,仅内部使用
- poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
- check:执行setImmediate的callback
- close callbacks:执行close事件的callback,例如socket.on("close",func)
其实我们是可以将上述的6个阶段缩减成3个阶段,来方便我们分析:
- timers:执行setTimeout() 和 setInterval()中到期的callback。
- poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段
- check:执行setImmediate的callback
会缩减后的第二阶段pool进行停留;
当check阶段有事情的时候,会立刻到第三阶段执行callback;
执行完毕check阶段的函数以后,会循环到timers阶段;
如果delay时间还没有到,就回重新回到poll阶段进行等待;
当delay时间到了以后,就会重新来到check执行callback,再来到timers阶段,执行回调函数。
总结
做面试题的时候一定要把几个阶段以及队列画出来,一个个用笔写出来。
面试题搜一下,做几道就ok了。