Will Javascript ES6 promise support 'done' api? - javascript

For example
p = new Promise(function (resolve, reject) {
throw 'err';
});
p.done();
In most promise polyfill libs, the done will throw an error, and the current execution will exit.
But if we use p.then(), nothing will happen. The error is swallowed by the promise. If we use p.catch, we have no way to exit current execution. I want to achieve something like:
try {
// something
} catch (err) {
if (check(err)) {
throw err;
}
}

No.
Not only will .done likely not be supported in the future versions of the spec - it is unneeded. Quoting from the threads Mariusz linked to:
Domenic:
it's still error-prone: if you slip up and don't follow the rule even once, you might silence an error forever.
Mark Miller (who pioneered the concept of promises):
Note that weak-refs, hopefully in ES7, will provide us one of the diagnostic tools we need to bridge this gap. Using weak-refs, if a rejected promise gets collected without having notified any handlers, we can arrange that this generates a diagnostic. The promise implementation would have to keep the reason in the promise's executor (post-mortem gc handler), so that it has the diagnostic to report after discovery that the promise has been rejected.
Yehuda Kats on RSVP's error handler:
The approach we're taking in RSVP is to install an unhandled promise monitor that throws by default.
You can opt a particular promise out of this behavior by attaching a noop failure handler, if you know that you will be attaching asynchronous error handlers. We will probably have sugar for this (.undone :p)
In our experience, moving the burden from literally everyone to people who may want to attach async error handlers is appropriate.
And, from the actual repo that preceded the spec, Domenic said:
done's job will be done by integrating unhandled rejection tracking functionality into dev tools. Most TC39ers, from what I understand, as well as myself, perceive that as enough for the spec to be complete.
The spec committee did not just ignore .done, they deemed it was unnecessary and error prone. New modern promise libraries automatically detect unhandled rejections - two examples of this are When promises and Bluebird promises that pioneered the idea.
.done is an artifact - originating from the fact the browser could not detect unhandled rejections. Truth is - detecting them deterministically is impossible but for the vast majority of cases it is completely possible.
Don't believe me? Open Firefox and play with its native promises:
p = new Promise(function (resolve, reject) {
throw 'err';
});
// Logs as error: Unhandled error: `err`
Simply put - firefox uses garbage collection hooks in order to determine promises were disposed in an unhandled state and fires a global error handler which defaults to writing on the screen.
Now, the problem is native promises are not very usable yet - since in IE they don't exist and in Chrome unhandled rejection detection was not yet implemented - but it's coming and it'll be there. Meanwhile you can use an ES6 compatible library like Bluebird which will do this rejection tracking for you.
If you want to polyfill done (which I strongly recommend against) - the polyfill by torazaburo has a few shortcomings. It declares an enumerable property on the promise prototype and generally this is not how the spec was designed - you are expected to subclass promises in order to extend them rather than monkey patch them - sadly no implementations currently support this.
So in short:
Wait for native promises to stabilize before you use them - in the meanwhile you can use libraries that implement the spec like Bluebird. When it stabilizes not having .done will not be an issue at all.
Utilize patterns for detecting errors - for example check out the disposer pattern here.
Use the developer tools when available, long stack traces and and async debugging are big plusses. Also note you should not throw strings if you want meaningful stack traces.
Good luck and happy coding.

No, AFAIK done is not part of the spec. To mimic its behavior, you should throw the exception on the next tick, outside the purview of the promises chain:
p.catch(function(e) {
setTimeout(function() { throw e; });
});
This is essentially how libraries implement done. See excerpt from Q documents:
Much like then, but ... the resulting rejection reason is thrown as an exception in a future turn of the event loop.
Implementing done yourself
If you want to implement the approximate semantics of done as typically understood, then something like:
Promise.prototype.done = function(onFulfilled, onRejected) {
this
.then(onFulfilled, onRejected)
.catch(function(e) {
setTimeout(function() { throw e; });
})
;
};
Setting up an error handler
If you want a chance to handle these errors yourself, you could set up an error handler:
Promise.onError = function(e) {
console.log("The sky is falling", e);
throw e;
};
Then invoke the handler on the next tick:
Promise.prototype.done = function(onFulfilled, onRejected) {
this
.then(onFulfilled, onRejected)
.catch(function(e) {
setTimeout(Promise.onError || function() { throw e; }, 1, e);
})
;
};

Current statement of TC39 is that this issue can be and should be solved natively in browser engines with developer tools. That's why they're opposite providing done within native API.
It's indeed controversial decision, see following links for discussions on that matter:
https://github.com/domenic/promises-unwrapping/issues/19
http://mozilla.6506.n7.nabble.com/Where-d-Promise-done-go-td281461.html
https://github.com/promises-aplus/promises-spec/issues/43
https://github.com/slightlyoff/Promises/issues/33

Related

Do promises in AngularJS catch every exception/error?

I've inherited a codebase at work that contains a dozen or so examples of the following pattern:
var promise = null;
try {
promise = backendService.getResults(input);
}
catch (exception) {
console.err(exception);
}
if (promise !== null) {
promise.then(function (response) {
// do stuff
})
.catch(function (error) {
console.err(error);
});
}
Where backendService is an Angular service that in turn calls a REST service through $http.
So here's my question: is that try/catch really necessary? Will there ever be any scenario where a particular error/exception is thrown that the promise's .catch fails to catch?
This has been the subject of a bit of debate on the team all morning, and the only resolution we've come up with is that we don't think it's necessary, but (a) changing it breaks the tests that were written alongside it (which would also need to be changed), and (b) well... it's defensive coding, right? It's not a Bad Thing.
The merits of actually bothering to refactor it into oblivion when there are more important things to do aren't what I'm asking about, though. I just want to know if it's a reasonable pattern when promises are being passed around like this (in AngularJS specifically, if that makes a difference), or just paranoia.
Do promises in AngularJS catch every exception/error?
No. Only exceptions that are thrown from inside then/catch callbacks are automatically caught. All errors happening outside of them will need to be handled explicitly.
Will there ever be any scenario where a particular error/exception is thrown that the promise's .catch fails to catch?
Yes. That backendService.getResults(input) call might not return a promise, but it can throw an exception. Or it doesn't even get that far when backendService is null, and you'll get a ReferenceError or .getResults is not a function and you'll get a TypeError.
is that try/catch really necessary?
Not really. In the latter case, that your code has a grave mistake, you probably don't care to throw and crash. The former case, that backendService.getResults(input) throws, is heavily despised. Asynchronous functions should never throw but only return promises - exactly for the reason that you don't have to write two error handling statements then.
well... it's defensive coding, right? It's not a Bad Thing.
Yeah, pretty much. But it is questionable here. A synchronous exceptions is really unexpected here, not just a service whose failure can be handled. The logging message in the catch block should signify that.
Notice that it also isn't defensive enough. It doesn't catch the probably more likely mistake where getResults() does return, but something that is not a promise. Calling .then() on that might throw. Similarly, the if (promise !== null) is dubious, as it hides when null is returned erroneously (we really could need try-catch-else here).
is that try/catch really necessary?
Not really. As long as your backendService.getResults() returns a promise. Like return $http(...)
Will there ever be any scenario where a particular error/exception is
thrown that the promise's .catch fails to catch?
I don't think so, since any error will be a rejected promise, it will fall into your .catch() handler
well... it's defensive coding, right? It's not a Bad Thing.
It depends... Javascript try/catch is has some performance issues. So if you're using just for the sake of making sure, you could remove it :)
Take a further look at try-catch discussion here if you wish: Javascript Try-Catch Performance Vs. Error Checking Code

Promise exception reported after random delay in Firefox

While doing some work I saw "uncaught exception" errors reported in Firebug appearing at seemingly random moments. The exceptions were thrown inside promises in background.
At first I was surprised the errors were reported at all, as my understanding was that errors thrown in promises are simply caught and passed along the callbacks chain. But they are reported in post-Opera (=Chrome) too, and they don't kill the script, so that is a good thing then.
But in Opera exceptions are reported immediately while in Firefox there is some seemingly random delay (from a couple of seconds to half a minute). Why is that?
Here is a test code:
var p = new Promise( okLater );
p.then( kill );
function okLater( pass, fail ) {
setTimeout( pass.bind( this, "O.K." ), 10 );
}
function kill() {
console.log( "Killing" );
throw "Oops"
}
At first I was surprised the errors were reported at all, as my understanding was that errors thrown in promises are simply caught and passed along the callbacks chain
Yes, they are indeed. That promise you have created via p.then(kill) is going to be rejected.
But we don't really want that. Exceptions that are silently ignored? Not a good idea. So we need unhandled rejection handling. An "unhandled exception" is a rejected promise that got no error handler attached. There is a bit of problem with identifying these, because the error handler could be attached later intentionally, and we wouldn't want that promise to be reported. Still, promise implementations have the capability to track their promises and report "possibly unhandled rejections", see this answer and How do I handle exceptions globally with native promises in node.js? for further details.
reported in Firebug appearing at seemingly random moments.
The moment you can be totally sure that a promise was rejected without a handler being attached is when it is garbage-collected. IIRC, Firefox has indeed implemented this hook, so these "random moments" would depend on the GC behaviour.

Asynchronous promise handling with Bluebird

For some reasons, I get a Bluebird promise to which I attach handlers asynchronously. For example:
var promise = Promise.reject(new Error('Handled rejection'));
setImmediate(function () {
promise.then(console.log, console.error);
});
Although my promise is well handled, Bluebird warns me about an unhandled rejection.
I can fool it by synchronously adding an empty rejection handler:
promise.catch(function () {});
However, this looks really hacky. Is there any proper way to handle this kind of situation?
EDIT: in answer to this comment, I posted a more detailed illustration of my real use case in this Gist. It also contains the workaround that I use while waiting the release of Bluebird 3.0. As Benjamin explained, Bluebird 3.0 will bring a solution with .suppressUnhandledRejection().
You can use global rejection events, specifically unhandledRejection, to silently consume the possibly unhandled rejection. However, this means that real unhandled rejections will also be silenced.
You can use rejectionHandled in conjunction with unhandledRejection to make your own decision about whether a rejection was handled or not.
Alternatively, you can be very careful about calling done at the end of every promise chain, in which case you can safely ignore the possibly unhandled rejections.
However, this looks really hacky. Is there any proper way to handle this kind of situation?
In 3.0 we have .suppressUnhandledRejection(). In 2.0 that's as far as you go. In my experience the situations where you actually need to attach handlers asynchronously are extremely rare and my first instinct is to suspect a problem with the code structure.
In either case, Aaron's answer already explains how to use the global rejection events which have the benefit of spanning across instances of bluebird and even other libraries throughout the code.

Errors in a promise's error handler go missing

If I throw an error in a promise's catch handler, the error is never bubbled up!
Promise.resolve()
.then(function() { return foo.bar(); })
.then(function() { return console.log('ok!'); })
.catch(function(err) { return baz.quux(); });
In both Node v0.8 with the promise module and Chrome 36's JavaScript console, this prints nothing. I'm expecting to see ReferenceError: baz is not defined appear somewhere.
Shouldn't we see something? Is this a part of the promises spec that I missed?
UPDATE: Clarification: this happens in Chrome 36's V8 without any third-party module.
Yes, this is a problem promise implementations face. However the two you've chosen fail at it pretty bad. Indeed - the error is swallowed in your case and indeed you will not get any indication of this silent failure unless you attach a error handler yourself.
Your options are:
Use a library that offers .done like Q and manually append .done to every single promise in your code to indicate you will not attach handlers to it.
Use a library like Bluebird or When that does unhandled rejection detection.
In particular, Bluebird is faster than native promises and does this properly. It is also a superset of native promises so you can write code that uses that subset if you'd like (although it has a much richer API). The code you have above logs an unhandled rejection with Bluebird.
By the way, Firefox handles this much better and Firefox native promises detect unhandled rejections.

What does it mean to "not get a callback at all" in Node.js error handling in TJ's farewell post?

Recently, I've been read the TJ's blog article: "Farewell Node.js".
I'm not quite understand about the Node fails part. Here it is:
Error-handling in Go is superior in my opinion. Node is great in the sense that you have to think about every error, and decide what to do. Node fails however because:
you may get duplicate callbacks
you may not get a callback at all (lost in limbo)
you may get out-of-band errors
emitters may get multiple “error” events
missing “error” events sends everything to hell
often unsure what requires “error” handlers
“error” handlers are very verbose
callbacks suck
What specific problem is being referred to when the author writes "you may not get a callback at all (lost in limbo)"?
It means the error is lost in limbo since the operating function did not "get a callback", viz., the error is "swallowed", since there is no callback to handle it.
var foo = function(onSuccess, onFailure) {
// ...
// uh-oh, I failed
if(onFailure) {
onFailure(err);
}
else {
// well, that probably wasn't too important anyway...
}
}
foo(function() { console.log("success!"); } /* no second argument... */);
Note in synchronous coding (say, most Java) it's much harder for this to happen. Catch blocks are much better enforced and if an exception escapes anyway, it goes to the uncaught exception handler which by default crashes the system. It's like this in node too, except in the above paradigm where an exception isn't thrown it's likely swallowed.
Strong community convention could solve it in my trivial example above, but convention can not completely solve this in general. See e.g. the Q promise library which supports a done method.
Q.fcall(promisedStep1)
.then(promisedStep2)
.then(promisedStep3)
.then(promisedStep4)
.then(function (value4) {
// Do something with value4
})
.catch(function (error) {
// Handle any error from all above steps
})
.done();
The done call there instructs the promise chain to throw any unhandled exceptions (if the catch block were missing, or the catch block itself throws). But it is fully the responsibility of the programmer to call done, as it must be, since only the programmer knows when the chain is complete. If a programmer forgets to call done the error will sit dangling in the promise chain. I have had real production bugs caused by this; I agree, it's a serious problem.
I'll be honest that a lot of that block in the post doesn't make much sense to me. But I'm an experienced Node.js programmer and this is the only thing I can think that could mean.

Categories

Resources