Proposal: Conditional `catch` in Promises

# Ayush Gupta (6 years ago)

I propose that in Promises, we accept another function which returns a boolean as an argument to catch. 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.

# T.J. Crowder (6 years ago)

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

jsfiddle.net/maov8y0h

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

# Ayush Gupta (6 years ago)

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.

# Isiah Meadows (6 years ago)

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.

# Michael J. Ryan (6 years ago)

Maybe approach typescript on this one... Not sure if that supports typed errors like C# does, but would probably suit you well.

# Bob Myers (6 years ago)

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

# Ayush Gupta (6 years ago)

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
# kai zhu (6 years ago)

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

# Isiah Meadows (6 years ago)

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