Breaking the function chain

I have a number of related functions on an object. For example:

var obj = {};
obj.a = function(){
    console.log('in a');  
    return this;
};


obj.b = function(){
    console.log('in b');  
    return this;
};

obj.c = function(){
    console.log('in c'); 
    return this;
};

obj.a().b().c();

      

Obviously the result when I look at the console:

 in a
 in b
 in c

      

But how would I break out of this to execute a

and b

but c

fail?

+3


source to share


4 answers


The obvious answer is "just do not name it c

", but I think that for some reason this is not an option.

As an aside, you can hack it by throwing an error from one of the function calls before c

, but I would not recommend it. The exception is error handling, not flow control [*].



var obj = {};
obj.a = function(){
    console.log('in a');  
    return this;
};


obj.b = function(){
    console.log('in b');  
    if(shouldBreak) {
      throw new Error('decided to break');
    }

    return this;
};

obj.c = function(){
    console.log('in c'); 
    return this;
};

try {
  obj.a().b().c();
} catch {
  console.log('broke early')
}

      

[*] if you are not in Python

+2


source


Just add a variable outside of your methods, let's say it's called a flag, defaults to true.

Any method you don't want to continue, just set this flag to false. Then check the flag before returning.



So something like:

var flag = true

function b() {
    flag = false
    return this
}

function c() {
    return flag ? this : null
}

      

0


source


The easiest way is the same as how you break out of any function call: throw an error.

var obj = {
    someCondition: false,

    a: function() {
        return this;
    },

    b: function() {
        if (!this.someCondition) {
            var error = new Error("A message");
            error.name = "SomeConditionError";
            throw error;
        }

        return this;
    },

    c: function() {
        return this;
    }
};

      

And then call the methods and handle the error.

try {
    obj
        .a()
        .b() // throws a "SomeConditionError" here
        .c();
}
catch (error) {
    if (error.name == "SomeConditionError") {
        // gracefully handle "SomeConditionError"
    }
    else {
        // rethrow errors you don't know how to handle gracefully
        throw error;
    }
}

      

The only thing you want to avoid is using exceptions for flow control .

If you need to call obj.a()

, then obj.b()

, but then conditionally call obj.c()

, then the calling code should handle that:

obj.a().b();

if (someCondition) {
    // Assign to "obj" just in case obj.c() returns a different object
    obj = obj.c();
}

      

It feels like ugly code (and that's a bit), but it suggests that any errors that occur in these method calls are catastrophic, showing error stops. If you have a complex operation that involves multiple method calls on the same object or many objects, consider encapsulating this word in command :

function DoSomethingCommand(obj) {
    this.obj = obj;
}

DoSomethingCommand.prototype = {
    constructor: DoSomethingCommand,

    execute: function() {
        this.obj.a().b();

        if (someCondition) {
            this.obj = this.obj.c();
        }
    }
};

      

From the point of view of the calling code, this is just a simple call execute()

to start a really complex process:

var command = new DoSomethingCommand(obj);

command.execute();

      

0


source


Set the class field to a()

or b()

to be validated c()

.

-1


source







All Articles