Basically I have a construction like;
// Library function
function foo(){
return new Promise((resolve, reject) =>{
// Do lots of stuff, like rendering a prompt
// On user action
if(userDidSomething){
resolve(user_input);
}
if(userCanceled){
// On user cancel
reject('User canceled');
}
}).catch("Default error (usually canceling whatever it is, which means; do nothing)");
}
// Function to call library function with predefined settings for this website
function bar(){
// Have some defaults set here, like which kind of prompt it should be
return foo();
}
// Where the function will actually be used
function baz(){
bar().then("do some things");
}
I've worked my way around this issue some years ago but unfortunately forgot how I actually managed to do that.
The goal: Have one standard catch
handle things for me on the library level. If I want to overrule it, I can always do that later. So I guess it's: Break the chain.
The problem: Having the catch
before the then
, causes the then
to be triggered once I have dealt with the catch
(which is; ignoring it, in this case)
My current solution: I'm using throw
on the library-level catch
, this causes the promise to throw an uncaught error exception
.
However, this completely clutters up my console with errors which aren't really errors.
The problem is that the entire resolve
/reject
of the promise is being handled by the library. That promise gets returned around and I only call it way later.
This is a function I'm calling about 300 times throughout my project, and I don't want to be putting custom error handling on every single one of those function calls when the handling of this should be "don't do anything, really".
What you are asking for is not really possible using native Promises because that's not how they were intended to be used.
You can, though, create a custom Promise class that does this. Note that muting all rejections is a bad idea because you won't see if your code errors out. To go around that, only a special value (SpecialPromise.CANCELED
) is treated differently. The code has to track whether a .catch
is attached to it. When the promise encounters the special value and it has no catch callback at the moment, it quickly attaches a no-op catch callback to silence the error:
class SilentPromise extends Promise{
constructor(executor){
super((resolve, reject) => {
executor(
resolve,
e => {
if(e === this.constructor.CANCELED && !this._hasCatch){
this.catch(() => {})
}
reject(e)
}
)
})
this._hasCatch = false
}
then(success, error){
this._hasCatch = true
return super.then(success, error)
}
catch(error){
this._hasCatch = true
return super.catch(error)
}
catchIfNotCanceled(error){
this.catch(e => {
if(e === this.constructor.CANCELED)
throw e
return error(e)
})
}
}
SilentPromise[Symbol.species] = SilentPromise
SilentPromise.CANCELED = Symbol('CANCELED')
You can convert existing promises between SilentPromise
and Promise
using SilentPromise.resolve()
and Promise.resolve()
.