javascript - Node.js singleton module pattern that requires external inputs -


normally might create simple singleton object node.js:

var foo = {};  module.exports = foo; 

or

function foo(){}  module.exports = new foo(); 

however

what best way make clean singleton module needs external variable initialization? end making this:

var value = null;  function init(val){   if(value === null){     value = val;   }    return value; }  module.exports = init; 

this way using module can pass in initializing value variable. way so:

function baz(value){ this.value = value; }  var instance = null;  module.exports = function init(value){  if(instance === null){     instance = new baz(value); }    return instance;  } 

there's 2 problems encounter this:

(1) minor, semantics wrong. can rename init getinstance, can't make same function literal mean "initialize , get" since different meanings. have have function 2 different things. create instance , retrieve , instance. don't since in cases need make sure argument initialize instance not null. multiple developers using module it's not clear if module has been initialized yet, , if pass in undefined module hasn't been initialized, become problem or confusing @ least.

(2) more important - in cases initializing baz asynchronous. example, making redis connection or reading file initialize singleton, or making socket.io connection. trips me up.

e.g. here module have consider ugly stores socket.io connection:

    var io = null;      var init = function ($io) {          if (io === null) {              io = $io;              io.on('connection', function (socket) {                  socket.on('disconnect', function () {                  });              });         }          return io;     };  module.exports = {     getsocketioconn: init }; 

the above module initialized so:

var server = http.createserver(app); var io = socketio.listen(server); require('../controllers/socketio.js').getsocketioconn(io); 

so looking design pattern allows create singleton module initialization process asynchronous. ideally won't have same function both initializing instance retrieving it. such thing exist?

i don't think there way create pattern solves problem perhaps making mistake of structuring code in way creating problem doesn't need exist- problem of initializing module value once, using 1 function both init instance , retrieve instance.

it sounds you're trying create module gets initialized in 1 place , uses shared resource initialization other users of module. semi-common need in real world.

first off, it's ideal if module can load or create things depends on because makes more modular , useful on own , puts less of burden on using it. so, in case, if module create/load thing needs when module first created , store resource in it's own module variable, ideal case. but, not possible because shared resource may else's responsibility set , initialize , module needs made aware of that.

so, common way use constructor function module. in javascript, can allow constructor take optional argument provides initialization info. code responsible setting module call constructor desired setup parameter. other users of module weren't responsible setting module either not call constructor or if want return value or there other constructor parameters should pass, pass null setup parameter.

for example, this:

var io;  module.exports = function(setup_io) {     if (setup_io) {         io = setup_io;     }     return module.exports; };  module.exports.method1 = function() {     if (!io) {         throw new error("can't use method1 until io initalized");     }     // code here method1 };  // other methods here 

then, users of module either this:

// load mymodule , initialize shared variable var mymodule = require('mymodule')(io); 

or this:

// load mymodule without initializing  // (assume other module initialize properly) var mymodule = require('mymodule'); 

note: developer sanity, useful have individual methods require appropriate setup (before can used properly) check see if module has been setup when method called needs setup in order inform developer have called method before setting module properly. otherwise, errors can happen further downstream , won't have useful error messages.


if want initialization process async, can done too, complicates other uses of module because won't know when/if module has been initialized.

var moduledata; var readylist = new eventemitter();  module.exports = function(arg, callback) {     // async operation here involving arg     // when operation completes, stored result     // in local module data , call callback     readylist.on("ready", callback);     someasyncoperation(arg, function() {         // set moduledata here         // notify else module ready         readylist.emit("ready");         // remove listeners since one-shot event         readylist.removealllisteners("ready");     });     return module.exports; }; 

if have other users of module wish notified when has finished initializing, can allow them register callback notified when module ready.

// pass callback method called // async when module ready module.exports.ready = function(fn) {     // if module ready, schedule callback     if (moduledata) {         setimmediate(fn);     } else {         readylist.on("ready", fn);     } }; 

if, reasons don't quite understand, want use same constructor both initialization , ready detection, can done, though don't think it's near clear using separate method ready detection:

var moduledata; var readylist = new eventemitter();  module.exports = function(arg, callback) {     // if both arguments passed, assume request module     // initialization     if (arguments.length === 2) {         // async operation here involving arg         // when operation completes, stored result         // in local module data , call callback         readylist.on("ready", callback);         someasyncoperation(arg, function() {             // set moduledata here             // notify else module ready             readylist.emit("ready");             // remove listeners since one-shot event             readylist.removealllisteners("ready");         });     } else {         // constructor called ready request         // arg callback         if (moduledata) {             // if module ready, schedule callback             setimmediate(arg);         } else {             // otherwise, save callback             readylist.on("ready", arg);         }     }     return module.exports; }; 

usage async initializing module:

// async initialization form var mymodule = require("mymodule")(somearg, function() {     // can use mymodule here }); 

usage loading module , getting notified when else has initialized it:

var mymodule = require("mymodule")(function() {     // can use mymodule here }); 

Comments

Popular posts from this blog

yii2 - Yii 2 Running a Cron in the basic template -

asp.net - 'System.Web.HttpContext' does not contain a definition for 'GetOwinContext' Mystery -

c# - MSDN OneNote Api: Navigate to never before opened page without opening a OneNote Application Window -