Comments (33)
Arggh. I guess github issues are not plain text.
https://github.com/johnjbarton/Purple/blob/master/tests/promises/throwInFulfilled.html
from q.
Agreed. However, swallowing exceptions is desirable behavior since it unifies synchronous and asynchronous exception handlers. I wavered on this for a while, but Irakli Gozashvili convinced me it was necessary for the flipside developer ergonomics.
The present stopgap is to terminate all promise chains with a .end() call.
function main() {
// foo returns a promise, but we don't look at the return so we lose.
return foo(promiseLoaded());
}
main().end();
I am playing with the idea of having another stop-gap solution: using console.log to write a live array to the console. I would then add exceptions to the array when they are swallowed, and remove them when they are handled.
It would be much more awesome to extend Firebug with something like Causeway, where I could use something like a console.events
API to expose the causal graph of promises: a bar for each promise from when it was created to when it was resolved, whether it was fulfilled or rejected, what other promises were waiting for it, the stack trace at the point of creation, the stack trace at the point of failure, that kind of thing.
(Issues are in markdown format. You can use Github’s proprietary extension to Markdown for literal bodies: three backticks on top and on bottom. If you want syntax highlighting, put the file extension on the same line as the initial backticks.)
from q.
How about a Q.debug = true mode? The problem with .end() is of course you fail to call it just in the cases you need it.
I suppose to be realistic the ergonomics of Q are pretty horrible in that the call stack goes from "oh, my code" to "OMG what is this code?" A big step could be showing the dev the chain you describe. What is Causeway? (This is one of the areas I want to work on, tho not in Firebug. Same issues face all other async libs like require.js.)
from q.
Causeway is a distributed debugger.
http://wiki.erights.org/wiki/Causeway
MarkM talked about it in Belgium recently. http://www.youtube.com/watch?v=w9hHHvhZ_HY
http://www.erights.org/elang/tools/causeway/
from q.
Thanks! Since we are in the same process I think we can do something non-post morteum
from q.
@johnjbarton I certainly have ambitions for a live visualization.
from q.
The way I handle this issue in my Q-like mini implementation of deferreds/promises is by logging any exception thrown from success/error callbacks in addition to rejecting the derived promises with the exception being the rejection reason. So essentially, my stance is that an exception unhandled in a callback represents a programming error and should be logged. To signal that derived promises should be intentionally rejected, one must return a rejected promise instead of a value or an exception.
So Q could be fixed to differentiate between these two scenarios (currently it does not):
- promise.then(function(val) { throw 'something went unexpectedly wrong' }).then(..); // log exception and reject derived promise
- promise.then(function(val) { return Q.reject('I am unable to compute the value')}).then(..); // don't log anything, just reject derived promise
This approach has been working quite well for me so far and I believe that it is in the "spirit" of deferreds/promises.
From a quick glance at the code, a good place to start would be to log these exceptions:
Keep in mind that this is a breaking change, because throwing an exception and returning a rejected promise is not the same thing any more. So developers would likely have to go back and fix the old code that merely relies on throwing, rethrowing or unhandling an exception.
from q.
@IgorMinar this is how Q worked about a year ago. Unfortunately it’s harder to go back than to go forward.
from q.
@kriskowal can share more details about why this logging was removed?
from q.
@IgorMinar I was logging the exception and forwarding a rejection. This led to dirty and confusing double-logging if the rejection was handled properly.
I was also of the mind that exceptions would be decreed to be programmer errors and rejections were for intentional errors. However, this did not pan out in practice. A lot of existing systems throw exceptions for non-error cases, and these do need to get trapped in rejections. Having all thrown exceptions reported in every frame is not an option, and terminating a services because of an asynchronously handled exception is not an option. It’s really quite tricky. It would be a much better option to just visualize the promise graph for debugging purposes.
from q.
@kriskowal if an existing system communicates a result of an action by throwing an exception then the success callback should expect that (since its part of the contract of the api that it relies on). Isn't it reasonable to expect the developer of the success callback to wrap the call into try/catch and reject return rejection if an expected exception is thrown?
I actually use an alternative method - I made the logger configurable, so at runtime I can choose to display the exception logs during development and in production I silence them.
from q.
@IgorMinar It really just isn’t practical. A lot of these functions only throw in exceptional conditions, so a cautious developer would have to try/catch every third party function.
I really am considering a gamut of solutions. One of them discussed earlier with @asutherland would be to allow the promise library to be instantiated with logging emitters. It’s still on the table if someone has the time.
from q.
Another useful causeway link (that I don't believe the above links reference) is a more recent JS version with live-ish examples:
http://code.google.com/p/causeway/
from q.
I believe this issue has played out. We have done()
as an ugly stopgap for explicitly surfacing programmatic errors. We will have promise debuggers soon, for the other cases.
from q.
@kriskowal is there a write-up on how to use done()
to avoid "exception swallowing"?
from q.
@axelson https://github.com/kriskowal/q#the-end
from q.
So I love Q, and prefer it, except this error handling case is really difficult on me.
https://github.com/tildeio/rsvp.js/#error-handling
In RSVP, they have an "error" event that fires, meaning as a user you can have some console.assert
thing show any unhandled errors. I'd love to have some way of surfacing all exceptions at least while developing... would this be something you'd be interested in having in Q? Even if as "Q.onError` if there's no native event bus. (I haven't yet looked at code.)
from q.
@randallb Q.onerror is a thing that exists.
from q.
well... i fail at reading docs then. Thank you very much.
from q.
Ah it'll only catch errors if you use the .done()
method. I was proposing trying to trigger some event handler whenever an error is caught in a promise chain.
from q.
@randallb Sorry, that went out too soon. It probably does not do what you want. Q.onerror gets called for unhandled rejections that would get rethrown. It does not report errors that have been caught but have not been handled. There is a limbo for errors, where they might be observed and handled in a future turn. In the trivial case:
var rejected = Q.reject(new Error('boo'));
Q.delay(1000, function () {
return rejected;
}).done();
This is too trivial a case. The problem will show up if you use promise queues or asynchronous iterators / readers, since the producer and the consumer operate independently.
var reader = Reader([1, 2, 3, 4])
.map(function (value) {
if (value % 2) throw new Error('how odd');
});
// later...
reader.next().catch(function (error) {
});
So while we could surface unhandled errors in the same event loop turn like RSVP, it would be noisy for legitimate cases.
I need to refresh my context, but there are ongoing plans to surface other events like, ondefer, onresolve, onreject, onhandle that would be able to drive an inspector that would show "pending deferred" promises and "rejected unhandled" promises for any given moment.
from q.
If Q
- held weak references to all promises upon creation,
- and strong references to all errors upon rejection,
- and dropped those error references whenever they're successfuly handled by a
catch()
clause,
couldn't we implement a function that runs once per event loop and throws unhandled errors if their corresponding promise has been garbage collected?
Weak references to promises that have been garbage collected but have no outstanding unhandled errors are just dropped as well.
Since this comes at a performance cost and not every JavaScript engine Q runs on would necessarily support ES6's WeakMaps or whatever we use to implement this, this feature could just be disabled (either explicitly by client code or implicitly, if Q detects weak references aren't supported), and people who either don't want it or can't use it can just keep using Q.done
.
Couldn't that work pretty well?
Granted, if the object takes too long to be garbage collected, the program may keep running for a while before the exception is logged and/or the process is killed, but that's better than nothing in my case, at least.
from q.
Yes, that would work wonderfully. Unfortunately weak maps are not weak references.
from q.
From https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/WeakMap:
Because of references being weak, WeakMap keys are not enumerable
(i.e. there is no method giving you a list of the keys). If they were, the list
would depend on the state of garbage collection, introducing non-determinism.
If you want to have a list of keys, you should maintain it yourself.
Are you talking about this? It looks like you can kind of do it if you maintain a list of things that should be on the WeakMap, and check them periodically, no?
from q.
How would you maintain that list? (Answer: using a strong reference.)
from q.
True. How about adding unique IDs to promises and keeping a list of those, though?
from q.
How would you then find out if the promise is in the weak map?
from q.
Crap, true. They're not enumerable. That sucks :(
from q.
@n2liquid WeakMap is what it is for good reasons and is deliberately not for this use-case. There is a proposed WeakRef that provides a post mortem notification callback. We had to wait for TC39 to specify an event loop to get Promise. We’ll have to wait for TC39 to specify garbage collection before we’ll get WeakRef. Maybe we’ll get it someday, but that sounds hard. Members TC39 would prefer not to introduce a feature that would increase the observable differences between engines and specifying GC could seriously paint the language into a corner.
from q.
@kriskowal I see, it's a very tricky thing, indeed. But boy, I just don't feel safe that even things like TypeErrors would just be swallowed if I forget to use Q.done
or yield the promise into a Q.spawn
generator.
I guess dumping unhandled errors to the console before exiting the program is as good as it'll get for me (I'm not writing a server, so this is reasonable in this particular case).
from q.
// In browsers, uncaught exceptions are not fatal.
// Re-throw them asynchronously to avoid slow-downs.
https://github.com/kriskowal/q/blob/v1/q.js#L158
I'm sorry, but really?? This is all kinds of wrong. Not only are we swallowing exceptions, we're destroying the stack too. I don't know what the solution is, but this ain't it.
from q.
I'm sorry, but really?? This is all kinds of wrong. Not only are we swallowing exceptions, we're destroying the stack too. I don't know what the solution is, but this ain't it.
I don't think rethrowing destroys the stack. The stack will be that of when the Error object was created.
from q.
e.stack
is available (only sometimes, it seems) but as a string, which means I can't step up from it in the debugger and inspect values, etc.
from q.
Related Issues (20)
- unhandledRejection does not provide promise.stack HOT 1
- Is it possible to use a native Promise object with q ? HOT 1
- async function giving error HOT 1
- Q.allSettled returning unhandled rejection promises HOT 3
- Question : Global Error handling in Promise HOT 2
- Issues with Promise prototype. HOT 1
- Is Q a superset of a native Promise? HOT 4
- How do u like await? HOT 1
- Invalid token q.js?v=1588706333081:854 Invalid token HOT 1
- Google Analytics HOT 7
- Safari drops ticks for backgrounded tasks iOS 14 HOT 13
- Minified release?
- The q4 and after is so hard to understand HOT 1
- In v2, not v1, package.json uses deprecated form of license {} HOT 5
- Add warning and recommend native promises HOT 1
- Why return function ref ? HOT 6
- How can i convert an existing promise to a callback in Node.js ? HOT 1
- Need 'qs' version upadted to latest version to resolve Security Vulnerabilities HOT 2
- rasamaha
- Mark as deprecated on npm? HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from q.