4 ms·
> You can't reasonably argue that this: try { foo = JSON.parse(input) } catch (er) { return "invalid data" } is more lightweight than: foo =
by azov 15y ago
> You can't reasonably argue that this:
try {
foo = JSON.parse(input)
} catch (er) {
return "invalid data"
}
is more lightweight than:
foo = JSON.parse(input)
if (!foo) return "invalid data
This is like saying that walking is faster then driving because I can walk 5 meters faster then it takes me to get into a car. Yes, error codes are more compact in a tiny "Hello world" example because it is only showing one function call. Exception handling becomes more compact when you're writing something less trivial and you don't have to repeat the same error handling code after every call.
> Try/catch is goto wrapped in pretty braces. There's no way to continue where you left off, once the error is handled.
Don't throw exceptions if you can handle the error and continue where you left off. Exceptions is for when you can't continue. Think of throwing as a way to roll back transaction, stop whatever you were trying to do, and go back to the last consistent state.
Obviously, exceptions are not perfect. As author correctly notes, they require careful consideration of what's exception and what's part of normal flow. But they were invented for a reason, and I don't see the article offering any alternative solutions to the problems that exceptions are solving now.
- dmethvin 15y ago> Don't throw exceptions if you can handle the error and continue where you left off. Exceptions is for when you can't continue. Yes, although quoting that example doesn't support the assertion. JSON.parse is a library function. How can it judge whether the caller can continue or not just because the JSON cannot be parsed? > Think of throwing as a way to roll back transaction, stop whatever you were trying to do, and go back to the last consistent state. Any try block that is larger than one atomic operation can become a nightmare to roll back, since the catch gives you no idea how far it was into the block, what resources were allocated, etc. So although try/catch avoids the hassle of checking state after each operation, you pay for it on errors.
- azov 15y ago> JSON.parse is a library function. How can it judge whether the caller can continue or not just because the JSON cannot be parsed? Don't make assumptions about the caller, throw if your library can't continue. > So although try/catch avoids the hassle of checking state after each operation, you pay for it on errors. If your language supports RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initial...) you get a free ride. Otherwise you have to write non-trivial cleanup code regardless of how you return errors. Try/catch (and especially finally) helps a lot because you get to group all your cleanup in one place. With error codes the code looks something like: a = acquire(A) if (!a) return b = acquire(B) if (!b) { release(a) return } c = acquire(C) if (!c) { release(a) release(b) return } ... This is a maintenance nightmare. It's hard to see what the code is doing because the real logic is hidden between piles of error handling stuff. If you add a new resource acquisition in the middle you have to go over every statement that follows and modify error handlers. And don't you dare changing the order of statements, because that basically forces you to rewrite the whole function. Believe me, you don't want this in your code, exceptions are your friend :)
- regularfry 15y agoNo, the way to handle that pattern is with nested gotos: a = acquire(A); if (!a) goto err_a; b = acquire(B); if (!b) goto err_b; c = acquire(C); if (!c) goto err_c; do_stuff(a,b,c); err_c: release(b); err_b: release(a); err_a: return; This is precisely why goto is not universally evil.
- gue5t 15y agoConsider: a = acquire(A); if (a) { b = acquire(B); if (b) { c = acquire(C); if (c) { do_stuff(a,b,c); } release(b); } release(a); } return;