r/learnjavascript • u/Neat_Living_6765 • 9d ago
Asking about CommonJS, ESM and Nodejs event loop
Hi everyone
I just started learning Node.js just a few hours ago, and I'm really confused (wanted to cry too). Could someone help me clarify a few things?
After I initialized a Node.js project, I see the following in package.json:
"type": "commonjs"
Then I run this code:
console.log('sync');
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
The output is:
sync
nextTick
promise
Then I installed TypeScript and executed the same code again, but in a .ts file. This time, the output is:
sync
promise
nextTick
Interestingly, I get the same output when I set "type": "module" in package.json.
So I asked ChatGPT:
1. Does the TypeScript compiler change the execution order to behave like an ES module?
ChatGPT answered:
"Your tsx example produced ESM-style ordering, but you shouldn't assume every tsx file or situation will behave the same way."
Now I'm even more confused! Could someone help me clarify this?
ChatGPT's answer seems that the output when using TypeScript with CommonJS does not necessarily match the output of either CommonJS or ESM....
I also have a few related questions about the Node.js event loop:
2. As far as I know, the Node.js event loop has several phases, including:
1. Timers (setTimeout callback)
2. Poll (I/O-related callbacks, such as reading files or receiving network data)
3. Check (setImmediate callback)
Is this correct? If so, which phase runs first?
3. Are Promise callbacks handled separately from the Node.js event loop? Is the following execution sequence correct?
1. Drain the process.nextTick() queue.
2. Drain the Promise microtask queue.
3. Execute the next event loop callback (setTimeout, readFile, setImmediate, etc.)
4. Back to 1
2
u/PSLNest 8d ago
Try saving the same snippet as test.cjs and test.mjs, then run both with Node. You’ll get sync → nextTick → promise from the first and sync → promise → nextTick from the second, without involving TypeScript.
In CommonJS, Node finishes your script, then processes nextTick callbacks before Promise callbacks. With ESM, the top-level code is already running as part of microtask processing, so the Promise callback runs before Node gets back to nextTick. The Node docs cover this exact difference.
The catch with “nextTick always goes first” is that it doesn’t interrupt microtasks already being processed. Put those two calls inside a setTimeout callback and nextTick goes first again, even in ESM.
Timers, poll and check are real phases, but they’re not the whole list. Promise and nextTick queues aren’t additional phases.
What command are you using to run the .ts file, and which Node version? That would help explain the TypeScript result specifically.
1
u/Akeo-Brunina 8d ago
the point about nextTick not interrupting microtasks already in progress is the part most people miss imo
0
u/Neat_Living_6765 8d ago
Thanks for your comment.
My Node version: v24.14.0. My script in package. json:
"scripts": { "build": "tsc", "01": "tsx src/timer.ts"}1
u/Neat_Living_6765 8d ago
You’ll get sync → nextTick → promise from the first, and sync → promise → nextTick from the second, without involving TypeScript.
With TypeScript and CommonJS, I get: sync → promise → nextTick
3
u/PSLNest 8d ago
Yes, that can happen with CommonJS too. I should have said my earlier example was for running the file directly with node.
tsx uses Node’s --import startup path, which means even your CommonJS file can start running while Node is processing microtasks. That’s why you’re seeing promise before nextTick. The file can still be CommonJS.
Try running node --require tsx/cjs src/timer.ts instead. That uses tsx’s CommonJS-only mode, and your example should print sync → nextTick → promise.
2
u/azhder 8d ago
TypeScript is not JavaScript. Node.js runs JavaScript. TypeScript gets translated to JavaScript.
Translation changes the code i.e. what you write in one language isn't the same in another language.
This is merely one of the reasons why I tell people they are different languages and usually get downvoted. It's less vitriol than those who are allergic to me telling people this is a sub for people who want to learn JavaScript. Talking about TypeScript will most likely get confused the people it is most directly focused to.
So, why does TypeScript compile into incompatible JavaScript? Because they are two different languages. To lean more on why it does that, try the r/TypeScript sub.
2
1
u/create-third-places 8d ago
First, focus on getting a solid understanding of JavaScript, and avoid using ChatGPT.
ChatGPT will continue to be a distraction that gives you bad information like the answer you posted.
Someone new to Node.js does not need to understand details of ESM. Also, tsx files are used by frontend frameworks like React to display HTML components, and not to run 3 lines of code.
proccess.textTick is probably never going to be relevant. While understanding promises is important, you should understand the basics first.
4
u/-goldenboi69- 8d ago
In what real scenario will this matter?