Search code examples
javascriptchaining

How do I exit a function cascade/chain?


If you create an object with functions that return this, you can create a chain of functions that were called previously and create a cascade of function calls:

var i = {
  foo: function () {
    // Something here...
    return this;
  },

  bar: function () {
    // Something here...
    return this;
  }
};

i.foo().bar().foo()

But what if an error occurs in bar and I don't want to call foo in that case? How do I break the cascade?

If it is at all possible, I would like to avoid try/catch statements.


Solution

  • Ok, so one straightforward thing is that if you want to handle the case without try/catch you have to put if condition in your functions and obviously you have to return something, so that you can execute further functions on that context instead of exception. So try creating all functionality in a object, and allow execution of your function logic only if someone extends. and on your failure return the base, otherwise return current object. In this was you can avoid creating objects every time.

    Example: Lets Consider You have BaseService where all functionalities are defined, and putting a Layer over it to just extend further, so you can go for this pattern:

    foo: function() {
        if(<function foo does not belongs to this>) {
            .......
            .......
            if(<on logical failure>) {
                return this.__proto__;
            }
            .......
            .......
        }
        return this;
    }
    

    Here is a working snippet:

    function BaseService() {
        var dataBucket = [13, 50, 45, 57, 95];
    
        this.foo = function() {
            if (Object.values(this).indexOf(arguments.callee) === -1) {
                console.log('processing foo');
            }
            return this;
        }
        this.bar = function() {
            if (Object.values(this).indexOf(arguments.callee) === -1) {
                console.log('processing bar');
            }
            return this;
        }
        this.processValIfExists = function(val) {
            if (Object.values(this).indexOf(arguments.callee) === -1) {
                console.log('processing processValIfExists');
                if (dataBucket.indexOf(val) > -1) {
                    //process the value further
                } else {
                    return this.__proto__;
                }
            }
            return this;
        }
    };
    
    //define the extended service, you can add further
    //functionalities here. eg: createing the dataBucket here from args 
    function WrapperService() {};
    
    WrapperService.prototype = new BaseService(); // just extend from any service and use
    
    var svc = new WrapperService();
    
    console.log('----------Executes All-----------');
    svc.foo().bar().processValIfExists(95).foo().bar();
    
    
    console.log('----------Executes upto processValIfExists-----------');
    svc.foo().bar().processValIfExists(100).foo().bar();

    Note that its just a different approach that came to my mind, and to check for the current invoking method I tried to make the code generic instead of checking if its instance of WrapperService in a BaseService function in order to avoid code coupling and can be extend from any other Service as well.