Skip to content Skip to sidebar Skip to footer

$q: Default Reject Handler

I want to return a $q instance so that if clients don't call 'then' with a reject handler, then a default one runs. E.g. assume the default is to alert(1) Then mypromise.then(funct

Solution 1:

Let's assume there was a way to do that.

So we have a magical machine that can always find out if .then is called with a function second argument for a specific promise or not.

So what?

So you can detect if someone did:

myPromise.then(..., function(){ F(); });

At any point from anywhere at any time. And have G() as a default action.

And...?

You could take a whole program containing lots of code P (1) and convert that code to:

var myPromise = $q.reject();
P; // inline the program's code
myPromise.then(null, function(){}); // attach a handler

Great, so I can do that, so?

Well, now our magical machine can take an arbitrary program P and detect if myPromise had a rejection handler added to it. Now this happens if and only if P does not contain an infinite loop (i.e. it halts). Thus, our method of detecting if a catch handler is ever added is reduced to the halting problem. Which is impossible. (2)

So generally - it is impossible to detect if a .catch handler is ever attached to a promise.

Stop with the mumbu-jumbo, I want a solution!

Good response! Like many problems this one is theoretically impossible but in practice easy enough to solve for practical cases. The key here is a heuristic:

If an error handler is not attached within a microtask (digest in Angular) - no error handlers are ever attached and we can fire the default handler instead.

That is roughly: You never.then(null, function(){}) asynchronously. Promises are resolved asynchronously but the handlers are usually attached synchronously so this works nicely.

// keeping as library agnostic as possible.var p = myPromiseSource(); // get a promise from sourcevar then = p.then; // in 1.3+ you can get the constructor and use prototype insteadvar t = setTimeout(function(){ // in angular use $timeout, not a microtask but okdefaultActionCall(p);// perform the default action!
});
// .catch delegates to `.then` in virtually every library I read so just `then`
p.then = functionthen(onFulfilled, onRejected){
    // delegate, I omitted progression since no one should use it ever anyway.if(typeof onRejected === "function"){ // remove default actionclearTimeout(t); // `timeout.cancel(t)` in Angular     
    }
    return then.call(this, onFulfilled, onRejected);
};

Is that all?

Well, I just want to add that cases where this extreme approach is needed are rare. When discussing adding rejection tracking to io - several people suggested that if a promise is rejected without a catch then the whole app should likely terminate. So take extra care :)

Post a Comment for "$q: Default Reject Handler"