try/catch/else
Well, since promise.then(/blah/).catch(/blah/) is already a thing, why not stick with that?
try { // blah } then(val) { // blah } catch { // blah } finally { // blah }
I added val
to the then since we might want to pass something from try
to it, just as promises do. Best way to do that might be for try
to act
as an expression block and implicitly return the last thing in it.
Sounds good in principle but I think the name is misleading.
Actually try/catch/else is the synchronous equivalent of
tryFn().then(elseFn, catchFn)
but then
has other implications (it
returns a new promise and the then
method actually takes two functions).
Calling it then
would imply it's equivalent to
tryFn().then(thenFn).catch(catchFn)
which is actually just the same as
the first example, not the try/catch/else.
I also think having the else
after the catch
makes the intention clearer
(just like finally
comes after catch
and else
).
What about the following pattern (labelled block + break)?
processSuggestions: {
let suggestions;
try {
suggestions = await fetchSuggestions();
} catch (e) {
alert('Failed to load suggestions');
break processSuggestions;
}
showSuggestions(suggestions);
}
that's nice. Whenever I faced this issue I always turned to the boolean flag variable. But this is strictly better. I expect to use it from now on.
I honestly wish engines (read: V8) didn't jave so much issue optimizing that. But yes, it's a very useful pattern.
On Thu, Feb 8, 2018 at 10:13 AM, Claude Pache <claude.pache at gmail.com> wrote:
What about the following pattern (labelled block + break)?
processSuggestions: { let suggestions; try { suggestions = await fetchSuggestions(); } catch (e) { alert('Failed to load suggestions'); break processSuggestions; } showSuggestions(suggestions); }
I don't mean to hijack this tread. I'm mostly curious why you opt or even suggest for that over putting it in a function and an early return? Like;
function processSuggestions() {
let suggestions
try {
suggestions = await fetchSuggestions();
} catch (e) {
return alert('Failed to load suggestions');
}
showSuggestions(suggestions);
}
This is almost identical, especially the way the example was written. I understand the differences, I don't think they're a problem for by far most cases where you'd want this.
That said I wouldn't mind seeing try/catch/else/finally because the pattern(s) above still leads to awkward code in the real world.
One could bikeshed on how "else" implies the attempt ("try") to have failed rather than succeeded. On the other hand making it try/then/catch/finally is also going to be confusing so whatever :)
I think the best argument for having try/catch/else is that it makes it trivial to translate promises into async/await. Consider this:
let result = a()
.then(b, c)
.catch(d);
If we want to translate this 1:1 to try/catch/else in an async function we'll end up with something like this:
try {
let x, y;
try {
x = await a();
} catch (e) {
y = await c(e);
} else {
y = await b(x);
}
return y;
} catch (e) {
return await d(e);
}
Doing this without else
would require one of the workarounds suggested upthread.
Le 9 févr. 2018 à 00:19, Peter van der Zee <ecma at qfox.nl> a écrit :
On Thu, Feb 8, 2018 at 10:13 AM, Claude Pache <claude.pache at gmail.com> wrote:
What about the following pattern (labelled block + break)?
processSuggestions: { let suggestions; try { suggestions = await fetchSuggestions(); } catch (e) { alert('Failed to load suggestions'); break processSuggestions; } showSuggestions(suggestions); }
I don't mean to hijack this tread. I'm mostly curious why you opt or even suggest for that over putting it in a function and an early return? Like;
function processSuggestions() { let suggestions try { suggestions = await fetchSuggestions(); } catch (e) { return alert('Failed to load suggestions'); } showSuggestions(suggestions); }
This is almost identical, especially the way the example was written. I understand the differences, I don't think they're a problem for by far most cases where you'd want this.
A function is strictly more complex than a block because you have to define it first, then to invoke it. As a consequence, it is more code to write and read. Unless you have a good reason for it (which is not suggested by the original problem), why would you prefer the more complex way?
I see this operator quite confusing, in my opinion it's a best practice
treat the functions (and errors) separately. If you want to ignore the
second error you can even create a silent
helper.
const treatedShowSuggestions = (suggs) => {
try {
showSuggestions(suggs);
} catch (e) {
// treat error
};
try {
const suggestions = await fetchSuggestions();
treatedShowSuggestions(suggestions);
} catch (e) {
alert('Failed to load suggestions');
}
or
const silent = (fn) => {
try {
fn();
} catch (e) {}
};
try {
const suggestions = await fetchSuggestions();
silent(() => showSuggestions(suggestions));
} catch (e) {
alert('Failed to load suggestions');
}
This isn't even a workaround, it's just the right approach for what you want. If you wanna to evict the separated functions you can just inline the try/catch in the main function.
The idea isn't to make the second call's exceptions silent, it's not to catch them (i.e. let them propagate).
On 02/08/2018 06:50, Alan Plum wrote:
I realise there is some ambiguity in using the else keyword for this (though I can't think of a meaningful opposite of "catch" either).
Indeed. You can't use 'else' without breaking existing behavior. For example:
if (foo) try {...} catch (e) {...} else {...}
Waldemar
Yikes, thanks for pointing that out. I guess this could be resolved by having a lower precedence for catch/else
than if/else
or by enforcing the sequence try/else/catch
(as try
without catch
or finally
is a syntax error).
If you did else
before catch
/finally
, that'd solve your problem. ;-)
The catch with finally
(no pun intended) is this: does/should it
execute before or after else?
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
Going with python's semantics, finally should execute after else because else behaves analogous to catch. It's basically a "nocatch".
On Tue, Feb 13, 2018 at 11:30 AM, Isiah Meadows <isiahmeadows at gmail.com>
wrote:
If you did
else
beforecatch
/finally
, that'd solve your problem. ;-)The catch with
finally
(no pun intended) is this: does/should it execute before or after else?
Logically it makes sense finally
would be after, so putting the else
after try
and before catch
/finally
makes sense for that reason as
well:
try {
console.log('a');
a();
} else {
console.log('b');
b();
} catch (e) {
console.log('c');
c();
} finally {
console.log('d');
d();
}
Then
- If
a
doesn't throw: a, b, d. - If
a
throws: a, c, d. - If
b
throws: a, b, c, d - If
a
andc
both throw: a, c, d - If
b
andc
both throw: a, b, c, d
else
doesn't make sense in English terms anymore, though. then
would
though, and on first blush would be consistent with what the promise
version of this would look like:
console.log("a");
a()
.then(() => {
console.log("b");
return b();
})
.catch(() => {
console.log("c");
return c();
})
.finally(() => {
console.log("d");
})
-- T.J. Crowder
On Tue, Feb 13, 2018 at 11:54 AM, T.J. Crowder <tj.crowder at farsightsoftware.com> wrote:
Then
- If
a
doesn't throw: a, b, d.- If
a
throws: a, c, d.- If
b
throws: a, b, c, d- If
a
andc
both throw: a, c, d- If
b
andc
both throw: a, b, c, d
Gah, I forgot that you don't want the else
block subject to the
catch
. So ignore that, and it no longer resembles the promise
version.
Don't like else
in front of catch
if exceptions from the code in
the else
isn't caught.
-- T.J. Crowder
Not quite. If the else
(or nocatch
, whatever) block throws, it would
bypass the catch
block but still hit the finally
block the same way
an exception in the catch
block would:
- if
a
doesn't throw: a, b, d - if
a
throws: a, c, d - if
b
throws: a, b, d - if
a
andc
throw: a, c, d
This is analogous to this promise equivalent:
console.log("a");
a()
.then(() => {
console.log("b");
return b();
}, () => {
console.log("c");
return c();
})
.finally(() => {
console.log("d");
})
(note the use of both arguments to then
)
Your reading wouldn't really provide anything that can't be done by simply moving the else block's content into the try block.
On Tue, Feb 13, 2018 at 12:37 PM, Alan Plum <me at pluma.io> wrote:
Not quite.
You appear to have missed my message three minutes later, after I'd realized that: esdiscuss.org/topic/try-catch-else#content-16
-- T.J. Crowder
Oops, you're right. I agree that else
might not be the best keyword for this all things considering.
I'm hoping this is the right medium to ask this but I would like to propose a language feature called try/catch/else.
Conditional try/catch seems to be all the rage right now but in many cases the problem it really wants to solve seems to be code like the following:
Having a more fine-grained catch wouldn't necessarily help here because both functions might throw the same kind of error but what we're really interested in is discerning the source of the error. So instead some people resort to something like this:
Note how we're forced to add a return to explicitly abort the control flow. Unlike the change from const to let this isn't something an IDE would point out while refactoring, so this actually introduces potential for bugs. If we don't actually want to bail out completely this often leads to noisy status booleans (e.g. didNotThrow) or error-prone checks (e.g. !suggestions is a bug if the async function really didn't return anything).
I'm not sure about other languages but Python has a solution for this by adding an else clause:
The semantics are pretty straightforward: if the try block did not throw, the else block is executed next (before the finally block if any). If the try block does throw, the else block is ignored (like a conditional catch that doesn't match).
I realise there is some ambiguity in using the else keyword for this (though I can't think of a meaningful opposite of "catch" either). There is also some overlap with conditional try/catch but I think this language feature is useful even if conditional try/catch exists.