Filtered Promise#catch

Jonathan Barronville jonathan at belairlabs.com
Tue Oct 10 23:46:33 UTC 2017


Yes, *+1*. Filtered .catch(…)
<https://web.archive.org/web/20171010234233/http://bluebirdjs.com/docs/api/catch.html#filtered-catch>
is
a *Bluebird* feature that I use *quite a lot*. Native support would be
great. I’ve personally only ever needed to use the constructor matcher
version (*i.e.*, the version which uses instanceof under the hood), but I
can see why support for the other versions (object predicate and function
predicate) could be useful.

On Tue, Oct 10, 2017 at 7:06 PM, Peter Jaszkowiak <p.jaszkow at gmail.com>
wrote:

> Excuse me if this has been discussed previously, I did try to find
> existing discussions.
>
> Bluebird has very useful functionality in `Promise.prototype.catch`, which
> allows for filtering certain error types. Here is an example:
>
> ```js
> database.get('user:Bob')
>   .catch(UserNotFoundError, (err) => {
>     console.error('User not found: ', err);
>   })
> ```
>
> Which is a shortcut for the following:
>
> ```js
> database.get('user:Bob')
>   .catch((err) => {
>     if (err instanceof UserNotFoundError) {
>       console.error('User not found: ', err);
>       return;
>     }
>
>     throw err;
>   })
> ```
>
> I think this would be a huge improvement to error handling in asynchronous
> situations, especially since many people dislike using `try { ... } catch
> (e) { ... }` syntax (which would also benefit from some sort of error
> filtering).
>
> ### Options
>
> I think that passing in the matching argument as the second argument is
> preferable to passing it as the first argument, but in the spririt of
> compatibility, supporting it in the Bluebird fashion is best.
> Alternatively, a new prototype method (like `catchFilter` or `error`) could
> be used to instead support the same functionality.
>
> Terminology:
> - Callback
>   The function passed as the next operation in the chain, to be called if
> a rejection occurs
> - Matcher
>   The argument passed to select a certain type of error to be caught, and
> then execute the provided callback
>
> #### Use `instanceof`
> The class or constructor would be passed as the matcher
>
> This on its own is  It would not support instances from other contexts,
> nor would it support custom errors created without subclassing (like
> manually setting error.name).
>
> Example
> ```js
> const err = new CustomError();
> Promise.reject(err)
>   .catch(CustomError, (customError) => {
>     // handle CustomError s
>   })
>   .catch((otherError) => {
>     // handle other errors
>   });
> ```
>
> #### Use pattern matching
> An object would be passed as the matcher, and it's own enumerable
> properties would be compared with properties of the error instance. If all
> properties on the given matcher are strictly equal to the same properties
> on the error instance, it's a match.
>
> Example:
> ```js
> const err = new Error();
> err.name = 'CustomError';
> Promise.reject(err)
>   .catch({ name: 'CustomError' }, (customError) => {
>     // handle CustomError s
>   })
>   .catch((otherError) => {
>     // handle other errors
>   });
> ```
>
> This would allow for matching any error type, and could support a subset
> of the `instanceof` check by doing something like `{ constructor:
> CustomError }`.
>
> #### Use a matcher function
> A function would be passed as the matcher, receiving the err as its
> argument. It would then be able to do any operation on the error instance
> to check if it is the correct error type.
>
> Example:
> ```js
> const err = new Error();
> err.name = 'CustomError';
> Promise.reject(err)
>   .catch(err => (err.name === 'CustomError'), (customError) => {
>     // handle CustomError s
>   })
>   .catch((otherError) => {
>     // handle other errors
>   });
> ```
>
> However, this is almost no different from just including the tests in the
> catch body itself. It's verbose enough that the benefit of supporting this
> is not nearly as significant than the other options.
>
> #### Support `instanceof` and pattern matching
> In my opinion, this is the best of both worlds. You get to support the
> basic `instanceof` case with inheritance, etc, while also supporting
> matching more custom errors made in a less standard manner.
>
> The method would check if the matcher is a function, and if so, it would
> use `instanceof`. Otherwise, it would treat the argument as an object and
> compare the properties.
>
>
> ### Example Naive Polyfill
>
> ```js
> const origCatch = Promise.prototype.catch;
> Promise.prototype.catch = { catch (ErrorType, callback) {
>   if (typeof callback !== 'function' && typeof ErrorType === 'function') {
>     callback = ErrorType;
>     ErrorType = null;
>   }
>
>   if (!ErrorType || !(typeof ErrorType === 'object' || typeof ErrorType
> === 'function')) {
>     return origCatch.call(this, callback);
>   }
>
>   // if the ErrorType is a function, use instanceof
>   if (typeof ErrorType === 'function') {
>     return origCatch.call(this, (err) => {
>       if (err instanceof ErrorType) {
>         return callback(err);
>       }
>
>       throw err;
>     });
>   }
>
>   // otherwise use pattern matching
>   return origCatch.call(this, (err) => {
>     const matches = Object.entries(ErrorType)
>       .every(([key, value]) => (err[key] === value));
>     if (matches) {
>       return callback(err);
>     }
>
>     throw err;
>   });
> } }.catch;
> ```
>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>


-- 
- Jonathan

—

Life is a game and we’re all just high density pixels.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20171010/3c1df664/attachment.html>


More information about the es-discuss mailing list