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?
source to share
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')
}
source to share
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
}
source to share
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();
source to share