DEV Community

Yuta Kusuno
Yuta Kusuno

Posted on

[JS] CommonJS vs. ES Modules: Synchronous and Asynchronous Module Loading

JavaScript employs two main module systems: CommonJS using require() and module.exports for imports and exports, and ECMAScript Modules (ESM), defined in the ECMAScript standard, utilizing import and export. CommonJS features synchronous module loading, while ESM brings asynchronous loading. I will explore and compare each module loading process.

CommonJS Example

In the CommonJS, module loading is synchronous. Consider the following code structure:

cjs.js:

console.log("cjs start");

require("./test1");
require("./test2");
require("./test3");

console.log("cjs end");
Enter fullscreen mode Exit fullscreen mode

test1.js:

console.log("test1");
Enter fullscreen mode Exit fullscreen mode

test2.js:

console.log("test2");
Enter fullscreen mode Exit fullscreen mode

test3.js:

console.log("test3");
Enter fullscreen mode Exit fullscreen mode

Execute the following command to observe synchronous module loading in action:

NODE_DEBUG=module node cjs.js
Enter fullscreen mode Exit fullscreen mode

The output would be:

CommonJS DEBUG

Let us break down the code and understand the flow:

  1. cjs.js: This is the main script that is executed. It starts by logging cjs start.
  2. Next, the contents of the test1.js is loaded and the JavaScript code is evaluated and executed. Then, similarly test2.js and test3.js are executed.
  3. While loading the modules synchronously, each module (test1.js, test2.js, and test3.js) logs its respective message.
  4. After all required modules are loaded and executed, the main script continues to execute and logs cjs end.

Unlike imports in ESM, require in CommonJS is synchronous and blocks the execution of the main script until the required modules are loaded.

ES Modules Example

In the ES Modules, the loading paradigm shifts to asynchronous. Consider the following code structure:

esm.js:

console.log("esm start");

import("./test1.js");
import("./test2.js");
import("./test3.js");

console.log("esm end");
Enter fullscreen mode Exit fullscreen mode

The content of test* files need not be changed. Execute the following command to confirm asynchronous module loading:

NODE_DEBUG=esm node esm.js
Enter fullscreen mode Exit fullscreen mode

The output would be:

ES Modules DEBUG

Let us break down the code and understand the flow:

  1. esm.js is the main script that is executed. It starts by logging esm start.
  2. Next, it uses dynamic imports to import and execute test1.js, test2.js, and test3.js. Dynamic imports are asynchronous, so they do not block the execution of the main script.
  3. While the dynamic imports are in progress, esm.js continues to execute and logs esm end.
  4. As the dynamic imports complete, each imported module (test1.js, test2.js, and test3.js) logs its respective message.

Therefore, the expected output reflects the asynchronous nature of dynamic imports, and you will see esm start and esm end logged first, followed by the logs from test1.js, test2.js, and test3.js.

CommonJS employs synchronous module loading, meaning that until one module is fully loaded, the loading of other modules is restricted. On the other hand, ESM Modules utilize asynchronous loading, enabling the concurrent loading of multiple modules. This characteristic is particularly advantageous as the number of modules to be loaded increases, potentially leading to improved performance.

That is about it. Happy coding!

Top comments (0)