The easiest way is the same way 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 one 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 needs to 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 uglier code (and it is somewhat), but this communicates that any errors thrown in those method calls are catastrophic, show-stopping errors. If you have a complex operation that involves multiple method calls on one object or many objects, consider encapsulating that in a "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 standpoint of the calling code, it's just a simple call to execute()
to kick off the really complicated process:
var command = new DoSomethingCommand(obj);
command.execute();