Handle Circular dependencies in JavaScript Modules
Circular Dependency:
Write a JavaScript program to create two modules that import each other and handle the circular dependency.
A circular dependency occurs when two or more modules depend on each other. Proper handling of circular dependencies is crucial to avoid runtime errors or unexpected behaviour.
Solution 1: Breaking Circular Dependency Using a Shared Module
Code:
File: moduleA.js
This module depends on a shared module and uses functions indirectly.
// moduleA.js
// Importing the shared module
import { sharedFunction } from './shared.js';
// Defining a function and exporting it
export function functionA() {
console.log('Function A in Module A');
sharedFunction('from Module A');
}
File: moduleB.js
This module also depends on the shared module.
// moduleB.js
// Importing the shared module
import { sharedFunction } from './shared.js';
// Defining a function and exporting it
export function functionB() {
console.log('Function B in Module B');
sharedFunction('from Module B');
}
File: shared.js
This shared module is used to decouple the circular dependency.
// shared.js
// Shared function to handle indirect communication
export function sharedFunction(message) {
console.log('Shared function called ${message}');
}
File: main.js
This file demonstrates the usage of the modules.
// main.js
// Importing functions from both modules
import { functionA } from './moduleA.js';
import { functionB } from './moduleB.js';
// Calling the functions
functionA(); // Logs "Function A in Module A" and "Shared function called from Module A"
functionB(); // Logs "Function B in Module B" and "Shared function called from Module B"
Output:
Function A in Module A Shared function called from Module A Function B in Module B Shared function called from Module B
Explanation:
- Modules moduleA.js and moduleB.js both rely on shared.js to avoid direct dependence on each other.
- The shared.js module acts as an intermediary for shared functionality.
- This approach breaks the circular dependency by introducing a shared abstraction layer.
Solution-2: Delayed Imports Using Functions
Code:
File: moduleA.js
This module exports a function that depends on moduleB.js.
// moduleA.js
// Defining and exporting a function that requires delayed access
export function functionA() {
console.log('Function A in Module A');
const { functionB } = require('./moduleB.js'); // Importing within the function
functionB(); // Calling functionB dynamically
}
File: moduleB.js
This module exports a function that depends on moduleA.js.
// moduleB.js
// Defining and exporting a function that requires delayed access
export function functionB() {
console.log('Function B in Module B');
const { functionA } = require('./moduleA.js'); // Importing within the function
functionA(); // Calling functionA dynamically
}
File: main.js
This file demonstrates the usage of the modules.
// main.js
// Importing functions from both modules
import { functionA } from './moduleA.js';
import { functionB } from './moduleB.js';
// Calling the functions
functionA(); // Logs both function outputs dynamically
functionB(); // Logs both function outputs dynamically
Output:
Function A in Module A Function B in Module B Function A in Module A Function B in Module B Function A in Module A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Explanation:
- Modules moduleA.js and moduleB.js import each other only when their functions are called.
- This delayed import mechanism avoids circular dependency issues by deferring execution.
- This approach works well when functions are used dynamically rather than during module initialization.
Improve this sample solution and post your code through Disqus
Previous: JavaScript Modules using Namespace Imports.
Next: Export JavaScript Functions Inline for Modular Code.
What is the difficulty level of this exercise?
Test your Programming skills with w3resource's quiz.
- Weekly Trends and Language Statistics
- Weekly Trends and Language Statistics