$q: Default Reject Handler
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"