关于 Node.js 中的事件循环问题。

14 天前
 autumnshine

大佬们,小弟最近在学习 Node.js ,发现 Promise.then 中的代码会先于 process.nextTick 执行,网上资料普遍说的是 process.nextTick 会先于微任务执行,请问这是什么原因...

有如下代码:

console.log("script start");

setTimeout(() => {
	console.log("setTimeout");
}, 0);

process.nextTick(() => console.log("nextTick"));

new Promise((resolve, reject) => {
	console.log("promise1");
	resolve(undefined);
	console.log("promise2");
}).then(() => {
	console.log("promise3");
});
console.log("script end");

执行结果为:

script start
promise1
promise2
script end
promise3     // 为什么会先于输出这个而不是 nextTick ??
nextTick 
setTimeout

直接使用 Node.js 执行 ts 文件,代码执行环境: Node:v25.2.1 TypeScript:5.9.3

tsconfig.json

{
	"compilerOptions": {
		"target": "ESNext",
		"module": "ESNext",
		"esModuleInterop": true,
		"forceConsistentCasingInFileNames": true,
		"strict": true,
		"skipLibCheck": true,
		"noImplicitAny": true,
		"noImplicitReturns": true,
		"strictNullChecks": true
	}
}

2194 次点击
所在节点    Node.js
30 条回复
ntedshen
14 天前
https://nodejs.org/en/learn/asynchronous-work/understanding-processnexttick
没问题吧,你现在实际上不就只有 setTimeout 在异步。。。
rabbbit
14 天前
node 直接执行试了下,输出如下
script start
promise1
promise2
script end
nextTick
promise3
setTimeout
autumnshine
14 天前
补充一下信息:package.json 中的"type": "module"。
autumnshine
14 天前
autumnshine
14 天前
@rabbbit 麻烦看看我回复的图片(捂脸。。。
autumnshine
14 天前
@ntedshen 有 Promise.then 。
rabbbit
14 天前
@autumnshine 看不到图片,我的梯子 ip 被 imgur 屏蔽了返回 403
autumnshine
14 天前
rabbbit
14 天前
试了下 node 25 ,看来这块有改动。

script start
promise1
promise2
script end
promise3
nextTick
setTimeout
ntedshen
14 天前
node20 也一样,在 type:module 和 type:commonjs 的情况下返回的顺序不同。。。
这应该是 esm 和 cjs 规范上的区别吧,感觉可能要去代码里翻了。。。
vace
14 天前
主要是 ESM 和 CJS 模块的解析差异造成的,CJS 是立即执行,ESM 模块的入口模块是一个异步 job ,此时先进入 microtask 所以先输出了 promise3 。

https://github.com/nodejs/node/issues/47319
autumnshine
14 天前
@vace 感谢大佬提供的信息源,谢谢。
jinlongguo
14 天前
这是个很好的问题!你遇到的情况确实和传统的 Node.js 事件循环理解不一样。关键原因在于 ES Module ( ESM )和 CommonJS 的执行差异。
问题根源
你的 tsconfig.json 配置了 "module": "ESNext",这意味着代码会以 ES 模块格式运行。在 Node.js 中:

CommonJS 模式:process.nextTick 确实优先于 Promise 微任务
ES Module 模式:微任务队列的处理时机不同,Promise 可能先于 nextTick 执行

验证方法
你可以做个对比实验:
1. CommonJS 版本(.js 文件)
js// test.js
console.log("script start");

setTimeout(() => {
console.log("setTimeout");
}, 0);

process.nextTick(() => console.log("nextTick"));

new Promise((resolve) => {
console.log("promise1");
resolve();
console.log("promise2");
}).then(() => {
console.log("promise3");
});

console.log("script end");
```

运行 `node test.js`,你会看到:
```
script start
promise1
promise2
script end
nextTick // ✅ nextTick 先执行
promise3
setTimeout
2. 修改你的 TypeScript 配置
将 tsconfig.json 改为 CommonJS:
json{
"compilerOptions": {
"target": "ESNext",
"module": "CommonJS", // 改这里
"esModuleInterop": true,
// ...其他配置
}
}
为什么会这样?
在 ES Module 中,模块的顶层代码本身就在一个微任务中执行,这会影响 process.nextTick 和 Promise 的相对顺序。Node.js 在处理 ESM 时,会在模块评估期间使用不同的微任务调度策略。
建议

如果需要严格控制执行顺序,使用 CommonJS 模式
如果必须使用 ESM ,理解这种行为差异,或者考虑使用 setImmediate 等其他 API
最佳实践:不要依赖 nextTick 和 Promise 之间的精确执行顺序,因为这在不同环境下可能不一致

你可以尝试修改配置后重新运行,应该就能看到符合预期的执行顺序了!

----答案来自 claude
chouvel
13 天前
前端都死了个球了,还玩这个八股文呢。
Wxh16144
13 天前
@jinlongguo 请不要在回答技术问题时复制粘贴 AI 生成的内容
h503mc
13 天前
@Livid #13 AI
Livid
13 天前
@Wxh16144
@h503mc

谢谢,13 楼那个大段复制张贴 AI 生成文本的账号已经被彻底 ban 。
tonytonychopper
13 天前
tonytonychopper
13 天前
@tonytonychopper 发现处理了,请忽略我
visper
13 天前
其实我觉得没必要分什么宏任务微任务,知道是同步异步就行了。谁敢靠这样的顺序来保证代码逻辑的,直接打死。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1176480

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX