Circular dependency, simple point is require B file in a file, then B file in turn require a file. We may not be aware of this problem at ordinary times, but it may cause some confusing problems if handled poorly. In node, how do you deal with the problem of circular dependencies?
Write a simple example to try and see.
Define two files:
A.js
var B = require ('./b ');
Console.log (' a.js get B: ' + b.b);
MODULE.EXPORTS.A = 1;</pre>
B.js
var a = require ('./a ');
Console.log (' B.js get A: ' + a.a);
module.exports.b = 2;</pre>
Perform
Node A.js
The result of the output is
B.js Get a:undefined
A.js Get B:2
From the printed trajectory, the process of executing the code is roughly as follows:
<pre>a.js:b.js:
var B = require ('./b ');
var a = require ('./a '); A = {}
Console.log (' B.js get A: ' + a.a);
module.exports.b = 2;
b = {B:2}
Console.log (' a.js get B: ' + b.b);
MODULE.EXPORTS.A = 1;</pre>
Node's loading process can be found in the Lib/module.js file. The code associated with this process is mainly focused on the Module._load method. As you can see, node creates a module object for each newly loaded file (assuming a), which is the module we see in the A.js code. After you create a, node places a in the cache and then loads the action on it. That is, if in the process of loading a, there is other code (assuming B) require a.js, then B can be taken directly from the cache to the module A, which will not cause a repeated loading of the dead loop. But the cost is that in the load process, B sees an incomplete a, which is why the front print undefined.
The constructor of the module
function Module (ID, parent) {
This.id = ID;
This.exports = {};
This.parent = parent;
This.filename = null;
this.loaded = false;
this.exited = false;
This.children = [];
}
Module._load method
Module._load = function (request, parent, Ismain) {
...
var module = new Module (ID, parent);
...
Module._cache[filename] = module;
try {
Module.load (filename);
} catch (Err) {
Delete Module._cache[filename];
throw err;
}
return module.exports;
};
This seemingly simple and crude approach, but in fact the node author weighed the results of various factors. Our beloved NPM author, Issacs, emphasizes that this is not a bug and will not change in the near future. Of course, Issacs also gives some suggestions to circumvent this trap. I conclude that there are two main points: one is to exports the things needed before the require that cause cyclic dependence, and the other is not to manipulate the unfinished modules during the load process .
so one way of dealing with the above example is to put the respective exports statements in front of the Require statement before running, and you can see that the correct values are printed.
From the previous analysis, the cyclic-dependent traps appear to be more demanding: one is a cyclic dependency and the other is to call an object that is not loaded during load. So people don't usually come across it. But I had a gorgeous encounter with this trap, here to take out as a negative case ...
The scenario is simplified roughly as follows: I have a bunch of service, each service is responsible for consuming a certain type of message, and may generate new messages for other service consumption. In terms of message passing, there is no circular dependency. But I've defined a message center role for message distribution for the understanding of decoupling. Center is mainly to maintain a type-a service map routing message, so that the center will have to load all the service in, resulting in a center->service dependency. On the other side, each service needs to distribute their new messages through the center, and then the Service->center relies on it, so the loop relies on it. Just in the service load process, but also called the center of a method, there has been a undefined error.
It is not difficult to solve the problem after it has been found out.
One way is to avoid the trap of cyclic dependence on the code level according to the previous method;
It is also possible to completely avoid the emergence of cyclic dependencies at the design level. My scenario has a cyclic dependency because both center and service need to know the other person's presence, the center <--a service. If you use a dependency injection, you can cut off this direct dependency, similar to the service of center <-container. Add a container role, load the center and service first, and then use the IOC method to set up the dependency relationship. So that the center and service do not need to know the other side of the specific files, it will not cycle require the other side.
In general, loop-dependent traps are not easy to come by, but once they do, they may not be very well positioned in the actual code. The existence of it gives us a wake-up call to pay attention to the dependencies in your project.
Node JS cyclic dependency problem