- From: Domenic Denicola <d@domenic.me>
- Date: Mon, 18 May 2015 23:26:07 +0000
- To: Elliott Sprehn <esprehn@chromium.org>
- Cc: WHATWG <whatwg@whatwg.org>, Boris Zbarsky <bzbarsky@mit.edu>, Brian Terlson <Brian.Terlson@microsoft.com>
The OP proposal. > -----Original Message----- > From: whatwg [mailto:whatwg-bounces@lists.whatwg.org] On Behalf Of > Elliott Sprehn > Sent: Monday, May 18, 2015 19:23 > To: Domenic Denicola > Cc: WHATWG; Boris Zbarsky; Brian Terlson > Subject: Re: [whatwg] An API for unhandled promise rejections > > What of the many things in that email are you considering? > > On Mon, May 18, 2015 at 3:49 PM, Domenic Denicola <d@domenic.me> > wrote: > > > Ping. We're considering implementing this in Chrome and it would be > > helpful to get a sense if other vendors support this. > > > > > -----Original Message----- > > > From: whatwg [mailto:whatwg-bounces@lists.whatwg.org] On Behalf Of > > > Domenic Denicola > > > Sent: Monday, February 9, 2015 20:45 > > > To: WHATWG > > > Cc: Petka Antonov > > > Subject: Re: [whatwg] An API for unhandled promise rejections > > > > > > An update on this: although the conversation somewhat fizzled here, > > > in > > io.js > > > (Node.js fork) something very similar is landing and I'm trying to > > > guide > > it > > > toward being reasonably compatible with browsers [1]. Additionally > > several > > > promise libraries have implemented similar hooks (see [2] and links > > > in > > the > > > comments; also of interest is the analysis of how promise-using > > libraries use > > > these hooks). > > > > > > The community largely settled on > > > unhandledRejection/rejectionHandled, > > > instead of hijacking the error event like the original post below > > proposes. > > > Which is not to say that we have to respect that in browsers, but > > > it's a > > data > > > point to consider. > > > > > > One interesting question that came up is the exact timing of the > > > unhandledRejection event. In my proto-spec at [3] I proposed queuing > > > a separate notify-rejected task for each rejection, largely because > > > it > > seemed > > > the easiest thing to spec. We now have some experience from the field. > > The > > > implementation at [1] considered a few approaches and cycled through > > > implementing some subset of them to make a progressively-larger set > > > of tests pass: > > > > > > - Queue a single task > > > - Queue a separate task per rejection (as in proto-spec) > > > - Queue a microtask that occurs after all other microtasks > > > - Queue a task that occurs after all other tasks > > > > > > Hopefully Petka, CC'ed, can correct me if I misstated these and fill > > > in > > any > > > details I missed. > > > > > > In general I am in favor of pushing off notification as long as > > > possible > > to give > > > more time for the rejection to potentially become handled (and thus > > > decrease false positives). From this perspective either separate > > > task per rejection or after-all-others task seems good. I was hoping > > > to get a web platform perspective on what sounds good and would be > implementable? > > > For example I think the after-all-others-task can be specced with a > > > new > > task > > > source that HTML mandates is drained after all others, right? > > > > > > Anyway, I mostly just wanted to give people an update and show that > > > we're prototyping this in io.js. Hopefully the interchange of ideas > > > here can > > help > > > push the progress in browsers too. > > > > > > > > > [1]: https://github.com/iojs/io.js/pull/758 > > > [2]: https://gist.github.com/benjamingr/0237932cee84712951a2 > > > [3]: > > > https://gist.github.com/domenic/9b40029f59f29b822f3b#promise-error- > > > handling-hooks-rough-spec-algorithm > > > > > > > > > -----Original Message----- > > > From: Domenic Denicola > > > Sent: Friday, September 12, 2014 14:34 > > > To: WHATWG > > > Subject: An API for unhandled promise rejections > > > > > > ## Problem > > > > > > A common desire in web programming is to log any uncaught exceptions > > > back to the server. The typical method for doing this is > > > > > > window.onerror = (message, url, line, column, error) => { > > > // log `error` back to the server > > > }; > > > > > > When programming asynchronously with promises, asynchronous > > > exceptions are encapsulated as _rejected promises_. They can be > > > caught and handled with `promise.catch(err => ...)`, and propagate > > > up through an "asynchronous call stack" (i.e. a promise chain) in a > > > similar manner to synchronous errors. > > > > > > However, for promises, there is no notion of the "top-level" of the > > promise > > > chain at which the rejection is known to be unhandled. Promises are > > > inherently temporal, and at any time code that has access to a given > > promise > > > could handle the rejection it encapsulates. Thus, unlike with > > > synchronous code, there is not an ever-growing list of unhandled > > > exceptions: instead, there is a growing and shrinking list of currently- > unhandled rejections. > > > > > > For developers to be able to debug promises effectively, this live > > > list > > of > > > currently-unhandled rejections certainly needs to be exposed via > > developer > > > tools, similar to how devtools exposes the ever-growing list of > > > unhandled exceptions (via console output). However, developer tools > > > are not > > sufficient > > > to satisfy the telemetry use case, i.e. the use case which is > > > currently > > handled > > > via `window.onerror` for synchronous code. > > > > > > ## Proposed Solution > > > > > > We propose that > > > > > > 1. `window.onerror` be extended to handle the rejected-promise use > > > case, notifying about any promises that, "at the end of the task > > > queue", > > contain > > > rejections that are not yet handled; and 2. A new hook, > > > `window.onrejectionhandled`, be added, to notify when (or if) such > > > rejections eventually become handled. > > > > > > By "at the end of the task queue" I mean that upon a promise being > > rejected > > > with no handlers, we would queue a task to fire an error event; > > > however > > if a > > > handler is then attached to a promise in the meantime, a flag would > > > be > > set so > > > that when the task executes nothing actually happens. > > > > > > ### Developer Experience > > > > > > In terms of developer experience, the result is that if a promise is > > rejected > > > without any rejection handler present, and one is not attached by > > > "the > > end > > > of the event loop turn", the resulting `(message, url, line, column, > > error, > > > promise)` tuple will hit `window.onerror`. If the developer > > > subsequently attaches a rejection handler to that promise, then the > > > `promise` object > > will > > > be passed to any handlers for the `rejectionhandled` event. > > > > > > As usual, if one or both of these events is missing listeners, > > > nothing > > will > > > happen. (In this case, the developer likely does not want to do > > telemetry on > > > errors, but instead will be availing themselves to the devtools.) > > > > > > A robust error-reporting system would use `rejectionhandled` events > > > to cancel out earlier `error` events, never displaying them to the > > > person > > reading > > > the error report. > > > > > > ### Specification Details > > > > > > We would extend [`ErrorEvent`](http://www.whatwg.org/specs/web- > > > apps/current-work/multipage/webappapis.html#the-errorevent- > interface > > > ) and `ErrorEventInit` with a `promise` member. Similarly, we would > > > extend the > > > [`OnErrorEventHandlerNonNull`](http://www.whatwg.org/specs/web- > > > apps/current- > > > work/multipage/webappapis.html#onerroreventhandlernonnull) > callback > > > type to take as its last argument that same promise. In both cases, > > > the promise would be `undefined` for synchronous errors. > > > > > > We would add a new event to the global, named `rejectionhandled`, > > > along with a `RejectionHandledEvent` class that contains only a > > > `promise` > > member. > > > > > > We would need to hook into rejecting promises and `then`-ing > > > promises, > > and > > > track unhandled rejections: > > > > > > * When a promise is rejected, if it has no handlers, we would queue > > > a > > task to > > > potentially-fire-an-error. > > > * When a promise is `then`'d (either by user code or by the spec's > > chaining > > > mechanisms) but the rejection has not yet been reported, we would > > > set a flag saying "don't fire that error after all." > > > * When the task is executed, if that flag is still unset, we would > > > then > > fire the > > > appropriate `error` event. > > > * If a promise is `then`-ed in such a way as to handle the > > > rejection, > > but that > > > promise had previously been reported as an unhandled rejection, we > > > would need to fire the appropriate `rejectionhandled` event. > > > > > > I can go into details on how to modify the promises spec to have > > > these hooks, if desired, as well as how HTML would exploit them to > > > maintain the appropriate list and report it at the end of the task > > > queue. I can also > > help with > > > the spec work here, on both the ES side and the HTML side, if desired. > > > > > > ### Potential Variants > > > > > > The `error` event and its idiosyncratic handler are not the best > > > possible extension points. We may be better off with a separate > > `unhandledrejection` > > > event (or, more accurately and as [popular > > > libraries](https://github.com/petkaantonov/bluebird/#error-handling) > > call it, > > > `possiblyunhandledrejection`). We could even unify on a single event > > class > > > used for both, e.g. `PromiseRejectionEvent` with members `promise` > > > and `reason`. This improves clarity and reduces piling kludges on > > > top of `window.onerror`, but requires any existing telemetry code to > > > upgrade to support the new event. > > > > > > I personally think this is a better solution, both because it has > > > less > > kludges > > > and because I can see server telemetry tools that aren't upgraded to > > > recognize the new duality becoming overwhelmed with useless `error` > > > events that are later canceled by `unhandledrejection` events they > > > are unaware of. That is, if you try to plug asynchronous errors into > > > your > > existing > > > telemetry systems, you will be pulling your hair out over spurious, > > > and sometimes hard-to-reproduce, errors in your logs. But other > > > members of > > the > > > Chrome team feel strongly about re-using onerror and I am happy to > > > let > > this > > > play out in the real world. > > > > > > Note that we are proposing this for the web, and not for ES, because > > > the web has `window.onerror` (not to mention an event system) > > > already. A more generic unhandled-rejection-tracking mechanism for > > > all ES environments might be something like an `Object.observe`able > > > `Promise.unhandledRejections` array, but that discussion can be left > > > for another time. > > > > > > ## Implementer Interest > > > > > > Chrome is interested in implementing this ASAP. I'm broaching the > > > idea > > for > > > the first time publicly to hopefully get other implementer interest > > > or > > at least > > > rough consensus :). > >
Received on Monday, 18 May 2015 23:26:37 UTC