Lazyloading JS modules in Titanium app

Sabtu, 1 November 2014, 1:03 pm24

In NodeJS project, usually modules are loaded at the beginning of the file:

var fs = require('fs');
var path = require('path');
var express = require('express');

When this NodeJS application being run, it loads all of the dependencies & start executing the program. In large application, there will be delay before the application can start operating, because of the dependencies loading.

For desktop or server application, this won’t be much issue, but in mobile app like Titanium, if it loads the whole dependencies during app launch, it might cause unresponsive app & consume large memory, even when those modules haven’t being used yet.

Another problem is there might be circular dependencies issue, in which module A require module B, but module B also require module A, and this lead to module A variable in B to become an empty object. Example:

// db.js - database module
var Person = require('model/Person');
Person.setup();

// Person.js - data model
var db = require('db');
db.execute('sql'); // TypeError, undefined is not a function

Restructuring the code flow is one method to fix, but in my experience, it cause a lot of repeated code

// Person.js
var Person = {};
Person.setup = function() {
     var db = require('db');
};

Person.getById = function() {
     var db = require('db');
};

Person.getAll = function() {
     var db = require('db');
};

Because of this, I use the lazyloading approach & make use of JS object getter to both solve the problem & make the code cleaner. The idea is to have a global variable, which will be included in each of the modules we have in a Titanium app, and this global object holds a reference to every modules we packaged into the app. This reference will then call a getter to do require() and return the actual module to the caller.

In addition, modules are categorized into folders which can act as namespace, to eliminate class name conflict

// globals.js
var g = {};

var modules = [
     'window/MainWindow',
     'ui/ContactList',
     'ui/ContactListItem',
     'model/Contact',
     'core/Db',
     'core/Http'
];

modules.forEach(function(mod) {
     var parts = mod.split('/');
     var namespace = parts[0];
     var className = parts[1];
     var obj = g[namespace];
     if (!obj) {
          obj = {};
     }
     Object.defineProperty(obj, className, {
          get: (function(path) {
               return function() {
                    return require(path);
               };
          })(mod),
          set: function() {}
     });
});

module.exports = g;

Now, the Person model class can be refactored like below:

// Person.js
var g = require('globals');
var Person = {};
Person.setup = function() {
     g.core.Db.execute('sql');
};
Person.getAll = function() {
     g.core.Db.execute('sql');
};

This is just a basic implementation of the idea, and you can extend it to support subnamespace & native module.

Tulis komen:

JS parseBool()

6 November 2014

Git download specific tag

14 Oktober 2014