Hello Fellow Dev!
Today, we'll discuss the differences between .mjs (ECMAScript modules) and .cjs (CommonJS modules) in Node.js. While modern frameworks like React, Next.js, and Vue often handle module support automatically, understanding these differences is crucial when working with Node.js directly, especially regarding the event loop and execution order.
My main goal for this discussion is towards the event loop and in the next sections, we will see some cases.
import fs from 'fs' import https from 'https'
const fs = require('fs') const https = require('https')
The Node.js event loop processes different queues with specific roles and priorities. Two important functions that affect execution order are process.nextTick() and setImmediate() and we use these time to time.
If you know the difference between process.nextTick vs setImmediate that's great if not then, a very basic idea
process.nextTick ensures that a piece of code runs after the current function but before any asynchronous I/O operations.
setImmediate schedules a callback function to be executed in the next iteration of the event loop, after any I/O events.
So current code -> process.nextTick -> any I/O operations -> setImmediate
Let's examine a code snippet that demonstrates the execution order:
//In case of mjs import https from "https"; import fs from "fs"; //In case of cjs const https = require("https"); const fs = require("fs"); setImmediate(() => { console.log("setImmediate callback"); }); process.nextTick(() => { console.log("nextTick callback"); }); fs.readFile("./async.cjs", (err, data) => { console.log("file IO Callback"); }); fs.readdir(process.cwd(), () => console.log("file IO Callback 2")); https.get("https://www.google.com", (res) => { console.log("https callback"); }); setImmediate(() => { console.log("setImmediate callback 2"); }); Promise.resolve().then(() => { console.log("Promise Callback"); }); process.nextTick(() => { console.log("Process nextTick console"); process.nextTick(() => { console.log("Process nextTick console 2"); process.nextTick(() => { console.log("Process nextTick console 3"); process.nextTick(() => { console.log("Process nextTick console 4"); }); }); }); }); Promise.resolve().then(() => { console.log("Promise Callback 2"); }); console.log("Main thread mjs"); Promise.resolve().then(() => { console.log("Promise Callback 3"); });
The code should run and execute in this way
Main thread mjs Promise Callback Promise Callback 2 Promise Callback 3 nextTick callback Process nextTick console Process nextTick console 2 Process nextTick console 3 Process nextTick console 4 setImmediate callback setImmediate callback 2 file IO Callback file IO Callback 2 https callback
But is it the case with mjs?
Not Really!
This is the output wrt mjs and cjs
Similar to process.nextTick and setImmediate, we can see the same behaviour with Promises as well.
Apparently, the difference in behaviour we're observing between the mjs (ECMAScript modules) and cjs (CommonJS modules) files regarding setImmediate and process.nextTick is due to how Node.js handles the event loop and microtasks in different module systems.
I have tested this behaviour in the Express and Nextjs app (dev mode) and interestingly, Express behaved like cjs and Nextjs behaved like mjs . The first set of logs are from Express and next are from Nextjs
Understanding the differences in execution order between .mjs and .cjs files is crucial when working directly with Node.js. I hope, this will help you understand the difference and execution of these functions wrt files, a little bit better. So next time when you play or try these functions in your app, keep these points in mind :)
For another example, please refer to the official Node.js documentation on the differences between ES modules and CommonJS file execution.
The above is the detailed content of mjs vs cjs with Event Loop. For more information, please follow other related articles on the PHP Chinese website!