I'm having this problem in ES6 in a Babel Environment:
// A.js
class A {
}
export default new A();
// B.js
import C from './C';
class B {
}
export default new B();
// C.js
import A from './A';
import B from './B';
class C {
constructor(A, B){
this.A = A;
this.B = B; // undefined
}
}
export default new C(A, B)
I import them like this:
// stores/index.js
import A from './A';
import B from './B';
import C from './C';
export {
A,
B,
C
}
And from my app entry point I do:
import * as stores from './stores';
I would (hoped) expected the order of execution to be A -> B ->C but in reality it is A-> C-> B.
This is due to module B importing C, so that the C module is evaluated before the B module. This creates a problem in the setup of C because in that case B would be undefined.
I've seen a similar question but I'm not sure the init function is the best solution here, it seems a bit hacky.
Q: What's the best practise about solving this kind of circular dependencies in ES6 that would possibly work in different environments (Babel, Rollup)?
Avoid them altogether. If you cannot (or don't want to) completely avoid them, restrict the involved modules to only use function declarations with circular dependencies, never initialise top-level values (constants, variables, classes) by using imported values (that includes
extends
references).The order of module resolution and initialisation is defined in the ES6 specification, so this should be the same in all ES6 environments - regardless how the modules are loaded and how their identifiers are resolved.
If you do have a circular dependency
X -> Y -> Z -> … -> X -> …
, you need to establish a start point. Say you wantX
to be loaded first, although it depends onY
, so you need to make sure thatX
never uses any of the imported values until all modules in the circle are completely initialised. So you break the circle betweenX
andY
, and you will need to start the import chain atY
- it will recursively traverse the dependencies until it stops atX
, which has no further dependencies that are not already getting initialised, so it will be the first module to be evaluated.The rule you have to follow then is to always import
Y
before importing any of the other modules in the circle. If you even once do not use this common single entry point to the circle, your house of cards will collapse.In your particular example, this means that
index.js
will need to use