4
votes

Here's the thing:

I'm using the CommonJS way of making my mobile (iPhone/Android) application modular. No surprise there. But there's one thing that I just can't get my head around.

CommonJS lets me create STATIC private variables, which lets me create singletons at ease. This, I think at least, is because the contents of a file that gets require()d is read only once and then the exports object (which is initialized only once) is returned every time.

But when I create a circular reference as seen below, the code inside the included module is executed every time.

Wait... Funny thing is, while I'm writing this question, I suddenly realise that none of the calls to require() finish before the next one starts (hence the stack overflow demonstrated below).

Any thoughts on whether I'm on track or not? It's past 5 AM over here, so all bets are off as far as I'm concerned :D.

EXAMPLES:

See this piece of code, it defines a singleton:

/* Singleton.js */

exports.getSingleton = getSingleton;

function getSingleton(name) {
  if (!instance) {
    instance = new Thing(name);
  }

  return instance;
}

function Thing(name) {
  this.name = name;
}

var instance;

I require() this file as such:

var theFirstThing = require('Singleton').getSingleton('first');
Ti.API.info('first: ' + theFirstThing.name)

var possiblyAnotherOtherThing = require('Singleton').getSingleton('second');
Ti.API.info('second: ' + possiblyAnotherOtherThing.name);

The Output is:

[DEBUG] loading: /path/to/sim/MyApp.app/app.js, resource: app_js
[DEBUG] loading: /path/to/sim/MyApp.app/Singleton.js, resource: Singleton_js
[INFO] first: first
[INFO] second: first

Why is it then that circular references like the following don't work? (I might have already ansered this myself, comment/answer on it if you like).

app.js

require('Banana');

Pinapple.js

require('Banana');

Banana.js

require('Pineapple');

Because the output is this:

[DEBUG] loading: /path/to/simulator/MyApp.app/app.js, resource: app_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Banana.js, resource: Banana_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Pineapple.js, resource: Pineapple_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Banana.js, resource: Banana_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Pineapple.js, resource: Pineapple_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Banana.js, resource: Banana_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Pineapple.js, resource: Pineapple_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Banana.js, resource: Banana_js

/* etcetera (total of 15 times back and forth) */

[DEBUG] loading: /path/to/simulator/MyApp.app/Pineapple.js, resource: Pineapple_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Banana.js, resource: Banana_js
[DEBUG] loading: /path/to/simulator/MyApp.app/Pineapple.js, resource: Pineapple_js
[ERROR] Script Error = Maximum call stack size exceeded. (unknown file)
1

1 Answers

9
votes

I too am using CommonJS modules in Appcelerator Titanium to build a mobile app. What I did to resolve the circular dependency problem was this: if A and B are the 2 circularly-dependent modules, require(A) in B, and vice-versa just before you actually need to use it. In my case, I needed A inside B only when a certain button got clicked, so I put a require(A) in B inside the button's click event listener. Hope that helps.