3

I am creating a module dependent on another async one. I'm trying to find the best way to expose my module so that the developer using it finds it intuitive.

Initially my module looked something like this:

var soap = require('soap');

var service = {};
soap.createClient('http://mysoapservice', function(err, client){
    service.myMethod = function(obj, callback){
        //dependency on soap module
        client.soapMethod(obj, function(err, data){
            //do something with the response
            //call other methods etc...
            var res = "processed "+data['name'];
            callback(err, res);
        });
    };
});
module.exports = service;

Of course the problem here is that calling mymethod before the callback inside the module code is reached will throw an exception.

What would be the best pattern to use for such a module?

Is returning promises specific to other libraries (q) an acceptable scenario? or should I just return a callback for when the module is initialized and let the developer handle the rest?

3 Answers 3

1

Firstly, I would not use singleton pattern. What if the user wants more than one service? It's better to have a constructor or a factory for this. This provides more design flexibility and makes things clearer for the user. For example:

// constructor
var Service = function() {
     this.initialized = false; // service is not initialized
};

Service.prototype.myMethod = function(...) {
     // check if initialized
     if (!this.initialized)
          return false;
     // do your stuff here
     ...
};

Service.prototype.initialize = function(..., cb) {
     // do your initialization and call the callback
     // also, set this.initialized to true
};

// want to have a factory, to provide a "createService" function?
// no problem
Service.createService = function(..., cb) {
     var service = new Service();
     service.initialize(..., cb);
     // service will only be usable once initialized, but the user
     // can still store a reference to the created object to use
     // in his callback
     return service;
};

module.exports = Service;

Use it like this:

// constructor + initializer
var myService = new Service();
service.initialize(..., function(...) {
    // we have the service, do stuff:
    myService.myMethod(...);
});

// factory method
var myService = Service.createService(..., function(...) {
    // we have the service, do stuff
    myService.myMethod(...);
});

In both cases, calling myMethod() before init will merely return false (instead of throwing).

I like this pattern because it's very similar to other patterns of popular modules out there, so users are used to it and know what to expect.

Sign up to request clarification or add additional context in comments.

5 Comments

I like the pattern, but the problem was not the exception throwing but the fact that the method call will not work during initialization. Is adding the myMethod function to a queue in the case of isInitialized=false acceptable? The queue could be checked inside the initialize method. Or am I complicating it for no reason?
I think that could work, yes, but it is a little overly complicated. The problem is that it might lead to issues if the user decides to call myMethod without even calling initialize. The next time you call initialize, the code in myMethod would execute, and that might be unexpected. I would leave that decision for the user.
What you could do is inherit your object from EventEmitter (one more reason to use this pattern), then you can emit an event (say, "init") when you are initialized. In this way, the user can listen to "init" events and do his stuff there. This is also a very common pattern.
Generally, I prefer documenting everything carefully so the user knows what to expect and allowing more flexibility than having obscure actions happening in the background to surprise the user. Have a look at the node API, I think it's a good example of how things should be done. This is a recurring issue when you have connections that need to be established and usually this is the way to handle it (either callbacks or events).
Ok, thanks a lot for the feedback, I'll dig deeper and find the best use case for embedding the soap npm. Any ideas on a best practices blog on modules?
0

What I ended up doing is this. Still not sure if it's a best practice but fits my needs. This way if you call the method before the initialization is over the function gets queued up.

/**
 * An async module
 * @module asyncModule
 */
 var q = require('q');
 var debug = require('debug')('asyncModule');

 var service = (function () {
    //private
    var obj;
    var init_defer = q.defer();

    //constructor
    var service = function () {
        var self = this;
        //initialize async module
        setTimeout(function(){
            obj = {};
            obj._method = function(message, callback){
                callback(message+" altered");
            };

            //resolve defer
            init_defer.resolve(obj);
        }, 5000);
    };

    service.prototype.myMethod = function (message, callback) {
        var self = this;
        //queue function
        init_defer.promise.then(function(obj){
            debug(self.name);
            obj._method(message, function(msg){
                callback(null, msg);
            });
        });
    };

    service.prototype.configure = function(options){
        this.name = options.name;
    };

    return service;
})();

module.exports = new service();

Comments

0

My solution ends up like this:

Rather than returning a service, return a promise of providing the service. The customer of the module needs to await for the service (module) get ready.

So the service module would be like

async function getAsyncResource() {
   // get something asynchronously
   // that the service depends on
}

...

export const serviceProvider = async ()=>{
   const someAsyncResource = await getAsyncResource()
   return () => {
      // this is the service function
      ... ...
      // which depends on the someAsyncResource
      someAsyncResource.doSomethingUseful()
      ... ...
   }
}

And the consumer of the service woud be like

import {serviceProvider} from 'serviceProviderModule'

...

async function doSomething() {
   ... ...
   // first get the service function asynchronously
   // then call the service function
   (await serviceProvider())()
   ... ...
}

This approach more aligns with functional programming and don't need to keep extra status such as if the module is initialised or not.

(The above code not necessarily 100% syntax correct, just gives an idea)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.