Proposal: Conditional `catch` in Promises
For my part, if we're going to get into optional catching, I'd rather see a
common approach between promise catch
and try
/catch
. There have been
various discussions about optional/filtered catch
on the list (here
and here, probably others).
Your approach is easily implemented in userland:
const cif = (predicate, handler) => e => {
if (predicate(e)) {
return handler(e);
}
throw e;
};
then
somePromise
.catch(cif(reason => reason instanceof ValidationError, reason =>
handleValidationError(reason))) // handle ValidationErrors
.catch(cif(reason => reason instanceof InternalError, reason =>
handleInternalError(reason))) // handle InternalErrors
.catch(cif(reason => handleOtherErrors(reason))); // catch all others
To be clear, I'm not saying that just because something can be implemented in userland, it shouldn't be considered. (Lots of things can be implemented in userland.) But again, I'd want to see a cohesive approach, and I think this probably doesn't take us in that direction.
-- T.J. Crowder
We could potentially provide the same functionality in try/catch
by
extending the signature of catch
to
try {
} catch(<expression_var>, <function_expression>) {
}
If <function_expression>
evaluates to truthy, invoke the catch
block,
otherwise don't.
I'd still prefer we wait until pattern matching [1] gets addressed first,
then tackling this. Error types are represented about 50 different ways in
JS, with subtyping only being one (used by the standard kind of). Node
appends an err.code
, and the DOM adds a similar type, just using a common
error subclass. And in some cases where errors are planned (but exceptions
are more convenient), you sometimes see non-errors thrown. So there needs
to be a means of catching all of them, and if
checks get verbose and
noisy in a hurry.
Maybe approach typescript on this one... Not sure if that supports typed errors like C# does, but would probably suit you well.
What do you mean by "approach TypeScript"? Do you mean propose this feature to the TS team? TS is not about new language features (with a few exceptions). It's about typing. They're quite careful about not forking the language.
Not sure if that supports typed errors
No, it doesn't.
Bob
It might be worth explicitly mentioning that it's not about types either, the benefit with using functions as the filter is that we can tackle a lot of cases. Consider this:
return somePromise
.catch((reason) => reason instanceof ValidationError, reason =>
handleValidationError(reason))
.catch((reason) => reason.code === 'ENOENT', reason =>
handleENOENT(reason))
.catch(reason => handleOtherErrors(reason)) // catch all others
On 26 Apr 2018, at 12:08 PM, Ayush Gupta <ayushg3112 at gmail.com> wrote:
It might be worth explicitly mentioning that it's not about types either, the benefit with using functions as the filter is that we can tackle a lot of cases. Consider this:
return somePromise .catch((reason) => reason instanceof ValidationError, reason => handleValidationError(reason)) .catch((reason) => reason.code === 'ENOENT', reason => handleENOENT(reason)) .catch(reason => handleOtherErrors(reason)) // catch all others
can someone give a sane javascript styleguide on when to use the above and when to use this code below? otherwise, we’re simply adding to the industry-painpoint of the language having too many cross-cutting design-patterns, leading to hard-to-maintain web-projects with inconsistent code across its components.
return somePromise
.catch((function (reason) {
if (reason instanceof ValidationError) {
return handleValidationError(reason);
}
if (reason.code === 'ENOENT') {
return handleENOENT(reason);
}
return handleOtherErrors(reason)) // catch all others
});
kai zhu kaizhu256 at gmail.com
I agree this isn't ideal (it's a design hack that wasn't very well
thought through on the part of Bluebird IMHO), but not on your
justification (which comes across as borderline off-topic). Most
complex exception handling with promises are effectively handled using
async/await, but I agree the simple stuff shouldn't be made more
complex without justification. If we're going to do type guards like
that, we have to run it in the same tick as the subsequent block, then
for efficiency purposes, what's the point of the guard callback? For a
change this substantial, I'm not convinced it is broadly useful enough
(unlike exception handling with guarded catch
blocks), especially
with async functions increasingly starting to replace most
promise-heavy code.
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
I propose that in
Promises
, we accept another function which returns aboolean
as an argument tocatch
. It will enable us to write code like this:return somePromise .catch((reason) => reason instanceof ValidationError, reason => handleValidationError(reason)) // handle ValidationErrors .catch((reason) => reason instanceof InternalError, reason => handleInternalError(reason)) // handle InternalErrors .catch(reason => handleOtherErrors(reason)) // catch all others
If the passed function evaluates to
true
, call the actual rejection handler. If none of the catch are eligible to handle the rejection, it causes an unhandled rejection.