How to prevent circular dependencies
March 23, 2019
Author: Dave Cohen
Some errors are kind of mysterious. The first time I got “‘myFunction’ is not a function” type errors, I was at a loss for a few hours. Logging out the functions from my require statements came back as {}
or undefined
.
What I learned, TL;DR - don’t import file ‘a’ into file ‘b’ AND file ‘b’ into file ‘a’. That’s an example of a “circular dependency.”
Brief example of creating a circular dependency:
// myUtils.js
const { validate } = require('./validators');
const myUtil = thing => console.log(validate(thing));
module.exports = {
thing,
};
// validators.js
const { myUtil } = require('./myUtils');
const validate = thing => typeof thing === 'string' && thing.length > 42;
const ultimateValidate = thing => {
myUtil(thing);
if (!validate(thing)) {
return { error: true, value: thing };
}
return { error: false, value: thing };
};
module.exports = {
validate,
ultimateValidate,
};
myUtils.js requires validators.js and vice versa. Can you see how this is “circular”? The require
system will resolve one file before another and does so to avoid a feedback loop where one file requires another, which requires the other, which requires the other…
How to avoid:
- Have file ‘a’ import dependencies not required in file ‘b’
- Have ‘main’ files which handle module integrations
- Use dependency injection
For our brief example, myUtil
could use dependency injection:
// myUtils.js
// here, we removed the require validators statement
const myUtil = (thing, validatorFunc) => console.log(validatorFunc(thing));
// module.exports = ...
// validators.js
// now we can safely require myUtil
const ultimateValidate = thing => {
myUtil(thing, validate); // <- we pass in validate function where it's in scope
...
};
Read more:
https://stackoverflow.com/questions/10869276/how-to-deal-with-cyclic-dependencies-in-node-js