为什么在Node.js中使用同步函数会被称为「阻塞事件循环」
1. 你对EventLoop的初始认知判断
这个理解整体是准确的,补充一点细节边界会更严谨:EventLoop是Node.js主线程的核心调度机制,除了负责异步IO操作完成后的回调调度传递到调用栈,还要处理定时器(setTimeout/setInterval)、setImmediate、process.nextTick等各类异步API的回调执行调度,覆盖的场景比单纯的异步IO回调要广。
2. 「同步方法阻塞事件循环」的逻辑解释
你这里的理解确实存在偏差,核心误区是把EventLoop和JS调用栈的运行当成了两个独立的并行流程,实际上Node.js的EventLoop和JS调用栈运行在同一个主线程上,二者是串行交替执行的关系,完全不存在调用栈执行同步代码时EventLoop还能正常运转的情况。
Node.js主线程的完整运行逻辑是固定的串行流程:
- 优先执行当前调用栈内的所有同步代码,直到调用栈完全清空
- 只有调用栈清空后,主线程才会进入EventLoop的各个阶段,遍历对应回调队列,把待执行的回调压入调用栈执行
- 回调执行阶段又是调用栈优先运行,执行完毕后再次回到EventLoop做下一轮调度
你调用pbkdf2Sync这类同步CPU密集方法时,调用栈会被持续占用,主线程根本没有机会进入EventLoop的调度流程,相当于EventLoop直接停转,所有已经完成的异步IO回调、到期的定时器、待执行的setImmediate任务都只能卡在队列里得不到执行,这就是大家说「同步方法阻塞事件循环」的原因。
你之前认为的「事件循环还能正常运行,异步任务完成后直接推回调入栈」是不成立的:异步任务的底层执行(比如libuv线程池跑加密计算、系统内核处理网络IO)确实是在后台独立线程/能力上运行,和主线程无关,但它们完成后只会把回调塞进对应队列,只有主线程空了、EventLoop跑起来的时候才会把队列里的回调取出来压入调用栈,只要调用栈被同步代码占着,这些回调永远不会被执行。
内容的提问来源于stack exchange,提问作者user1702436

