How to Handle Circular Dependencies with Requirejs/Amd

How to handle circular dependencies with RequireJS/AMD?

This is indeed a restriction in the AMD format. You could use exports, and that problem goes away. I find exports to be ugly, but it is how regular CommonJS modules solve the problem:

define("Employee", ["exports", "Company"], function(exports, Company) {
function Employee(name) {
this.name = name;
this.company = new Company.Company(name + "'s own company");
};
exports.Employee = Employee;
});
define("Company", ["exports", "Employee"], function(exports, Employee) {
function Company(name) {
this.name = name;
this.employees = [];
};
Company.prototype.addEmployee = function(name) {
var employee = new Employee.Employee(name);
this.employees.push(employee);
employee.company = this;
};
exports.Company = Company;
});

Otherwise, the require("Employee") you mention in your message would work too.

In general with modules you need to be more aware of circular dependencies, AMD or not. Even in plain JavaScript, you have to be sure to use an object like the G object in your example.

How to handle circular dependencies?

UPDATE: https://stackoverflow.com/a/42264822/14731 contains an updated answer for ES6 modules.


I figured it out: We need to create a "gatekeeper" file that will define functions that depend on the circular dependencies.

  1. Rename ObjectPreconditions.js to AbstractObjectPreconditions.js.
  2. Create a new ObjectPreconditions.js file (our new gatekeeper).
  3. Move any circular dependencies out of AbstractObjectPreconditions.js into ObjectPreconditions.js
  4. User code should require(ObjectPreconditions). Code involved in the circular dependency (e.g. subclasses) should require(AbstractObjectPreconditions).

Here is what the resulting code looks like:

define(["AbstractObjectPreconditions"], function(ObjectPreconditions)
{
console.log("Inside StringPreconditions");

function StringPreconditions() {}
StringPreconditions.prototype = Object.create(ObjectPreconditions.prototype);
StringPreconditions.prototype.constructor = ObjectPreconditions;
return StringPreconditions;
});

define(["require"], function(require)
{
console.log("Inside AbstractObjectPreconditions");

function ObjectPreconditions() {}
return ObjectPreconditions;
});

define(["AbstractObjectPreconditions"], function(ObjectPreconditions)
{
// Gatekeeper for circular dependencies
ObjectPreconditions.prototype.isInstanceOf(type)
{
console.log("ObjectPreconditions.isInstanceOf() invoked");
if (type === String)
return new StringPreconditions();
}

return ObjectPreconditions;
});

define(["ObjectPreconditions", "StringPreconditions"], function(ObjectPreconditions, StringPreconditions)
{
console.log("Inside Preconditions");
var Preconditions = {};

Preconditions.requireThat(parameter) = function()
{
return new ObjectPreconditions(parameter);
};
return Preconditions;
});

define(["Preconditions"], function(Preconditions)
{
console.log("Inside User code");
function User() {}
User.prototype.doSomething = function()
{
var StringPrecondition = Preconditions.requireThat("test").isInstanceOf(String);
}
});

Resolving circular dependencies for requireJS

is there a tool that will read through all my files (the .ts or the .js) and flag the places where I have circular dependencies?

Atom-TypeScript can do circular dependency analysis : https://github.com/TypeStrong/atom-typescript/blob/master/docs/dependency-view.md#circular

Also this looks interesting : https://www.npmjs.org/package/madge

Is there a way to handle this in requireJS

You get undefined initially but then you can require again : http://requirejs.org/docs/api.html#circular

Is the answer to have the typescript compiler create a single .js file from all the .ts files? And if so, is there any downside to this approach, both when debugging and in production?

Yes --out. -ves : slower compile times, must use sourcemaps for debugging. inability to lazy load parts of the codebase at production times.

Typescript + requirejs: How to handle circular dependencies?

Specify the type using what you get from import i.e

import dataModelType = require("Modules/dataModel");

class MyClass {

dataModel: typeof dataModelType;

RequireJS, Circular Dependencies and Exports Magic Method

Edit: I couldn't find more info about the magic exports method. I could, however, mimic its intended behavior with a dummy "Container" module. See it in this fiddle: http://jsfiddle.net/amenadiel/a7thxz98/

console.log("start");

define("Container",function() {
var Container={};
return Container;
});

define("Employee", ["Container"], function(Container) {
var Employee= function(name) {
this.name = name;
this.company = new Container.Company(name + "'s own company");
};
Container.Employee = Employee;
});

define("Company", ["Container"], function(Container) {
var Company=function(name) {
this.name = name;
this.employees = [];
};
Company.prototype.addEmployee = function(name) {
var employee = new Container.Employee(name);
this.employees.push(employee);
employee.company = this;
};
Container.Company = Company;
});

define("main", ["Container","Employee","Company" ], function ( Container) {
var john = new Container.Employee("John");
var bigCorp = new Container.Company("Big Corp");
bigCorp.addEmployee("Mary");
console.log(bigCorp);
});

require(["main"]);

Requirejs and asynchronous & circular dependencies

Louis' answer was interesting but it just moved the problem creating a new module, and even if it placed the dependencies in the right context, I tried to search for some real asynchronous module definition (not just loading).

The best I could finally come up with was to edit the require.js source file to add the behaviour I expected. I registered a new handler (a special dependency behaviour like "require" or "exports" ) called "delay" that provided a callback function for the module definition :

basically, this works this way :

define(["delay"], function ( delay ) {

var Module = ... ;

setTimeout(function(){ //Some asynchronous actions
delay(Module) ; //This does actually returns the Module's value and triggers the "defined" event.
//It's actually transparent so the other modules just "feel" that the network's load time was a bit longer.
},1000);

return Module ; //This does not do anything because delay is active

});

Thanks to this asynchronous definition, I can now scan the directory (an asynchronous request) and then transparently load all the modules.
The modifications represents only about 10 lines so if someone is interested, I post it there :
https://github.com/jrburke/requirejs/pull/1078/files

Apparently it's not the first request for asynchronous exports but it's still in debate so I just use this for personal projects.



Related Topics



Leave a reply



Submit