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.
I think this is quite a drawback in larger projects where (multi-level) circular dependencies dwell undetected.However, with madge you can print a list of circular dependencies to approach them.
madge --circular --format amd /path/src
If you don't need your dependencies to be loaded at the start (e.g., when you are extending a class), then this is what you can do: (taken from http://requirejs.org/docs/api.html#circular)
In the file a.js
:
define( [ 'B' ], function( B ){ // Just an example return B.extend({ // ... }) });
And in the other file b.js
:
define( [ ], function( ){ // Note that A is not listed var a; require(['A'], function( A ){ a = new A(); }); return function(){ functionThatDependsOnA: function(){ // Note that 'a' is not used until here a.doStuff(); } }; });
In the OP's example, this is how it would change:
define("Employee", [], function() { var Company; require(["Company"], function( C ){ // Delayed loading Company = C; }); return function (name) { this.name = name; this.company = new Company(name + "'s own company"); }; }); define("Company", ["Employee"], function(Employee) { function Company(name) { this.name = name; this.employees = []; }; Company.prototype.addEmployee = function(name) { var employee = new Employee(name); this.employees.push(employee); employee.company = this; }; return Company; }); define("main", ["Employee", "Company"], function (Employee, Company) { var john = new Employee("John"); var bigCorp = new Company("Big Corp"); bigCorp.addEmployee("Mary"); });