I was reading the Node.js design patterns book when I came across this section of cycles in module resolution. This is the sample code:
Module a.js:
exports.loaded = false;
var b = require('./b');
module.exports = {
bWasLoaded: b.loaded,
loaded: true
};
Module b.js:
exports.loaded = false;
var a = require('./a');
module.exports = {
aWasLoaded: a.loaded,
loaded: true
};
Module main.js:
var a = require('./a');
var b = require('./b');
console.log(a);
console.log(b);
To summarise, main.js
requires 2 modules, a.js
and b.js
. they internally require each other.
We know that require
is a synchronous function, it has to run to completion but if it has cycles in it, how does it work? and if it doesn't whats the resolution steps like?
Here is the output if you run main.js
:
{ bWasLoaded: true, loaded: true }
{ aWasLoaded: false, loaded: true }
These are CommonJS modules, and we can see the usage of exports and module.exports in the code. If you are not sure about these, read up this stack overflow answer: https://stackoverflow.com/questions/7137397/module-exports-vs-exports-in-node-js
based on the output, here is my assumption:
bWasLoaded
is true which mean the require to b was successful and it reached the point where we set loaded to true via module.exports.aWasLoaded
is false, so does that mean that a did not load fully? in this case how is the exports variable exposed? loaded is true because the require call for b was completed.
So here is what is happening:
main calls require('./a').
entry created in require.cache for
a.js
. as part of the module load,loaded
is set to false initially.b.js
is required froma.js
. No entry present forb
so its added torequire.cache
. loaded is set to false initially.a.js
is required fromb.js
. cache entry present fora.js
so that is returned. Mind that a.loaded is still false. Which is why the return value of module b is{ aWasLoaded: false, loaded: true }
. which also means that the exports variable for b was updated.control comes back to step 3. the return value for
a.js
becomes{ bWasLoaded: true, loaded: true }
.main calls require('./b'). Since the module is already cached, it does not reload the module and
aWasLoaded
always remains false.
This is a good primer on how module.exports, module caching works in nodejs specifically for CommonJS modules.