外观
Node 的事件循环和浏览器有什么不同?
Node 的主线程按阶段处理回调:定时器、挂起的回调、空闲、轮询、setImmediate、关闭回调。每个阶段结束后会去清微任务。process.nextTick 的队列比 Promise 的微任务更早。
原理
setTimeout 在 timers 阶段。setImmediate 在 check 阶段,一般晚于当前轮询。在 I/O 回调里,setImmediate 往往先于 setTimeout(fn, 0)。在主模块里两者的先后不要依赖,启动时机不同结果会变。
process.nextTick 会在当前操作结束后立刻执行,并且可以再往这个队列里加任务。队列不空,事件循环进不了下一阶段,I/O 会被饿死。能用 Promise 就不要用 nextTick 去递归排队。
浏览器没有 setImmediate 和 nextTick。微任务仍然是 Promise。讲面试题时先问清运行环境,再画阶段。见 Event Loop。
追问
同步代码会不会插进阶段中间?
当前 JavaScript 栈不空,事件循环不会取下一个回调。一个阶段里如果回调又同步做了很久的计算,后面的阶段都要等。
nextTick 和 queueMicrotask 谁先?
同一轮里先排空 nextTick,再排空 Promise 微任务。然后才能进入下一个事件循环阶段。
易错点
- 用浏览器那张「宏任务、微任务」图直接套 Node 的全部 API。
- 在
nextTick里继续nextTick,接口不再接受连接。 - 断言
setTimeout(0)一定早于setImmediate。