Code Monkey home page Code Monkey logo

Comments (33)

johnjbarton avatar johnjbarton commented on May 19, 2024

Arggh. I guess github issues are not plain text.
https://github.com/johnjbarton/Purple/blob/master/tests/promises/throwInFulfilled.html

from q.

kriskowal avatar kriskowal commented on May 19, 2024

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.

johnjbarton avatar johnjbarton commented on May 19, 2024

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.

kriskowal avatar kriskowal commented on May 19, 2024

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.

johnjbarton avatar johnjbarton commented on May 19, 2024

Thanks! Since we are in the same process I think we can do something non-post morteum

from q.

kriskowal avatar kriskowal commented on May 19, 2024

@johnjbarton I certainly have ambitions for a live visualization.

from q.

IgorMinar avatar IgorMinar commented on May 19, 2024

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):

  1. promise.then(function(val) { throw 'something went unexpectedly wrong' }).then(..); // log exception and reject derived promise
  2. 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:

q/q.js

Line 506 in 8ff517e

} catch (exception) {

q/q.js

Line 514 in 8ff517e

} catch (exception) {

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.

kriskowal avatar kriskowal commented on May 19, 2024

@IgorMinar this is how Q worked about a year ago. Unfortunately it’s harder to go back than to go forward.

from q.

IgorMinar avatar IgorMinar commented on May 19, 2024

@kriskowal can share more details about why this logging was removed?

from q.

kriskowal avatar kriskowal commented on May 19, 2024

@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.

IgorMinar avatar IgorMinar commented on May 19, 2024

@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.

kriskowal avatar kriskowal commented on May 19, 2024

@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.

asutherland avatar asutherland commented on May 19, 2024

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.

kriskowal avatar kriskowal commented on May 19, 2024

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.

axelson avatar axelson commented on May 19, 2024

@kriskowal is there a write-up on how to use done() to avoid "exception swallowing"?

from q.

kriskowal avatar kriskowal commented on May 19, 2024

@axelson https://github.com/kriskowal/q#the-end

from q.

randallb avatar randallb commented on May 19, 2024

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.

kriskowal avatar kriskowal commented on May 19, 2024

@randallb Q.onerror is a thing that exists.

from q.

randallb avatar randallb commented on May 19, 2024

well... i fail at reading docs then. Thank you very much.

from q.

randallb avatar randallb commented on May 19, 2024

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.

kriskowal avatar kriskowal commented on May 19, 2024

@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.

guiprav avatar guiprav commented on May 19, 2024

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.

domenic avatar domenic commented on May 19, 2024

Yes, that would work wonderfully. Unfortunately weak maps are not weak references.

from q.

guiprav avatar guiprav commented on May 19, 2024

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.

domenic avatar domenic commented on May 19, 2024

How would you maintain that list? (Answer: using a strong reference.)

from q.

guiprav avatar guiprav commented on May 19, 2024

True. How about adding unique IDs to promises and keeping a list of those, though?

from q.

domenic avatar domenic commented on May 19, 2024

How would you then find out if the promise is in the weak map?

from q.

guiprav avatar guiprav commented on May 19, 2024

Crap, true. They're not enumerable. That sucks :(

from q.

kriskowal avatar kriskowal commented on May 19, 2024

@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.

guiprav avatar guiprav commented on May 19, 2024

@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.

constfun avatar constfun commented on May 19, 2024
// 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.

guiprav avatar guiprav commented on May 19, 2024

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.

constfun avatar constfun commented on May 19, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.