Lorraine Bernard
Lorraine Bernard

Reputation: 13400

Circular Dependencies in modules using requireJs

Reading the requireJs documentation,
in order to fix the Circular Dependencies, is suggested to use exports to create an empty object for the module that is available immediately for reference by other modules.

I try this code but it seems to do not work. What is wrong?

P.S.:
read the comments for seeing the output,
especially the B module inside setTimeout call.


// A module
define([
    'b'
], function (b) {
    console.log('B:', b); // B, Object
    var A = {
        boo: 1
    };

    return A;
});

// B module
define([
    'a',
    'exports'
], function (a, exports) {
    console.log('A:', a); // A, undefined (as I was expecting)
    exports.A = function () {
        return a;
    }

    var B = {
        bar: 1
    };

    setTimeout(function () {
        console.log('exports.A', exports.A()); // exports.A undefined 
                                           // I would like to access the A object
                                           // which is defined in A module
    }, 500);

    return B;
});

// main.js

(function () {

    define([
        'a'
    ], function () {
    });
}());

Upvotes: 9

Views: 4201

Answers (3)

Stepan Riha
Stepan Riha

Reputation: 1724

You should be able to use the synchronous version of require() in your B module to access the "A" module:

// B module
define([
    'a',
    'exports'
], function (a, exports) {
    console.log('A:', a); // A, undefined (as I was expecting)
    exports.A = function () {
        return require('a');
    }
    ...
});

Upvotes: 1

Leo
Leo

Reputation: 293

One option would be not to return the module itself, but a function that instantiates the module (in this example it would a constructor as defined in typescript, at the bottom is the generated js code -note that interfaces do not generate .js code)

  • File IA.ts

    /// <reference path="IB.ts" />
    interface IA{
        funcA();
        _classB : IB;
    }
    
  • File IB.ts

    /// <reference path="IA.ts" />
    interface IB{
        funcB();
        _classA : IA;
    }
    
  • File ClassA.ts

    /// <reference path="IA.ts" />
    /// <reference path="IB.ts" />
    
    export class ClassA implements IA
    {
        _classB : IB = null;
    
        constructor(classB : IB)
        {
            this._classB = classB;
            if (classB){
                this._classB._classA = this;
            }
            return this;
        }
    
        funcA(){
            console.log('I am ClassA');
        }
    }
    
  • File ClassB.ts

    /// <reference path="IA.ts" />
    /// <reference path="IB.ts" />
    export class ClassB implements IB
    {
        _classA : IA = null;
        constructor(classA : IA)
        {
            this._classA = classA;
            if (classA){
                this._classA._classB = this;
            }
            return this;
        }
        funcB(){
            console.log('I am ClassB');
        }
    }
    
  • File MainTest.ts

    /// <reference path="../../def/require.d.ts" />
    /// <reference path="IA.ts" />
    /// <reference path="IB.ts" />
    define(['ClassA', 'ClassB'],
        function (classA, classB)
        {
            var aa : IA = new classA.ClassA();
            var bb : IB = new classB.ClassB(aa);
    
            bb.funcB();
            aa._classB.funcB();
            bb._classA.funcA();
            aa.funcA();
        });
    

And the generated js code:

  • File ClassA.js

    define(["require", "exports"], function(require, exports) {
        var ClassA = (function () {
            function ClassA(classB) {
                this._classB = null;
                this._classB = classB;
                if (classB) {
                    this._classB._classA = this;
                }
                return this;
            }
            ClassA.prototype.funcA = function () {
                console.log('I am ClassA');
            };
            return ClassA;
        })();
        exports.ClassA = ClassA;
    });
    
  • File ClassB.js

    define(["require", "exports"], function(require, exports) {
        var ClassB = (function () {
            function ClassB(classA) {
                this._classA = null;
                this._classA = classA;
                if (classA) {
                    this._classA._classB = this;
                }
                return this;
            }
            ClassB.prototype.funcB = function () {
                console.log('I am ClassB');
            };
            return ClassB;
        })();
        exports.ClassB = ClassB;
    });
    
  • File MainTest.js

    define(['ClassA', 'ClassB'], function (classA, classB) {
    
        var aa = new classA.ClassA();
        var bb = new classB.ClassB(aa);
    
        bb.funcB();
        aa._classB.funcB();
        bb._classA.funcA();
        aa.funcA();
    
    });
    

finally, the output will be:

I am ClassB

I am ClassB

I am ClassA

I am ClassA

Upvotes: 0

SimplGy
SimplGy

Reputation: 20437

I often have circular issues using AMD modules to build an application core that both stands up many modules and contains config or other useful objects for those modules to use.

I did some experimenting today and this seems to work pretty well.

define(['exports', 'underscore', './config', './mediator'],
  function (exports, _, Backbone, config, Mediator){

    Core = /* ... */

    // Publicize a core 'singleton' so that it's dependencies can access it, and so can modules that define it as a dependency themselves.
    core = new Core()
    exports.core = core //publicize it in a way that supports circularity
    return core // And also publicize it normally
  }
)

The objects are both '===' equal to each other, so this seems very promising.

EDIT:

The above method doesn't work when optimized. Here's another method that may (untested): https://github.com/requirejs/example-multipage/blob/master/www/js/app/main1.js#L2

define(function (require) {
  var $ = require('jquery'),
      lib = require('./lib'),
      Core;

   Core = /* ... */

   return new Core()
});

Upvotes: 0

Related Questions