Go functional - use a helper function with three callbacks:
function Try(attempt, onSuccess, onFailure) {
try {
var res = attempt();
} catch(err) {
return onFailure(err);
}
return onSuccess(res);
}
This allows you to write
return Try(() => …, x => {
// rest of the code that uses `x`
const y = x + …;
}, e => void e);
You can also make use of a data structure representing this control flow, like the Result
monad (also known as the Either
monad):
class Result {
constructor(go) {
this.go = go;
}
static Ok(v) {
return new this((onSuccess, _) => onSuccess(v));
}
static Err(r) {
return new this((_, onFailure) => onFailure(v));
}
map(f) {
return this.go(v => Result.Ok(f(v)), r => Result.Err(r));
}
chain(f) {
return this.go(v => f(v), r => Result.Err(r));
}
unwrap() {
return this.go(v => v, r => { throw r; });
}
}
function Try(attempt) {
try {
var res = attempt();
return Result.Ok(res);
} catch(e) {
return Result.Err(e);
}
}
You could use it very similar to the above simple helper function:
return Try(() =>
… // exceptions in here are caught
).go(x => {
// rest of the code that uses `x` - exceptions are not caught
const y = x + …;
}, e => void e);
But also with more advanced chaining:
return Try(() =>
… // exceptions in here are caught
).chain(x =>
Try(() =>
x + … // exceptions in here are caught as well
)
).map(y =>
… // exceptions in here are not caught
).unwrap(); // any caught exceptions are re-thrown