New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 3093 link

Starred by 51 users

Issue metadata

Status: Fixed
Owner:
Closed: Feb 2015
Cc:
HW: ----
NextAction: ----
OS: ----
Priority: 1
Type: FeatureRequest

Blocking:
issue chromium:348919
issue chromium:393913



Sign in to add a comment

Debugging promises

Project Member Reported by jakearchibald@chromium.org, Jan 10 2014

Issue description

Uncaught rejections are currently silent failures, which isn't great.

Having a stack track in the console for uncaught rejections would be a big win. Although, I'm not completely clear how that can be done since catches can be added asynchronously. Other promise libraries use done() for this, but we don't have that.

https://github.com/domenic/promises-unwrapping/issues/19#issuecomment-23787062 - the suggestion is to report uncaught errors on GC, but that may not happen until unload/exit if the promise remains in scope. Any ideas?

In terms of graphical debugging, the ember guys have some ideas https://github.com/tildeio/ember-extension/pull/76#issuecomment-31666151, but having a simple error logged for terminal use would be a great start (for node.js too).
 
Cc: paulir...@chromium.org slightlyoff@chromium.org aandrey@chromium.org rossberg@chromium.org dslomov@chromium.org
Labels: Harmony Type-FeatureRequest Priority-Medium
Status: Accepted
Perhaps logging to the console should happen when a promise is failed but does not have an (explicit) catch handler by the end of the same turn? I assume the cases where a handler is added later than that are pretty rare, and can probably be avoided if the message is not wanted.
Works for me. Will keep an eye out for cases where this does the wrong thing.
Apparently late-adding catch handers isn't pretty rare https://github.com/domenic/promises-unwrapping/issues/19#issuecomment-32021399

Not sure what the alternative is yet.

Comment 4 Deleted

Please do not log unhandled errors to the console immediately, unless there is a way to *remove* them from the console once they become handled. That would be a huge disincentive for developers for using promises for asynchronous control flow, which often involves asynchronous error handling.
early example of some tooling ideas + implemention: https://github.com/tildeio/ember-extension/pull/76
What do you think about logging when an end-of-chain rejected promise is GC'd?

I'm worried about inconsistencies like some errors not being logged for difficult to understand reasons (cannot be GC'd due to still-in-scope or something like eval() in scope), or errors being logged out of order.
Logging upon GC is great! It has no downsides, only upside. It just isn't a complete solution, since GCs have no obligations to collect promises. I would definitely think you would do that no matter what else was going on---GC is the time when you *know* something in the live list of unhandled rejections will never be removed from that list.
Well, the downsides are providing a potentially confusing log of events, by logging in the wrong order. I just don't know how bad it would be.
Unfortunately logging on GC will kill performance. V8 does not visit garbage in general, so this will require some sort of finalization mechanism for promises which is expensive. Maybe the damage can be minimized here though (the only promises requiring "finalization" are rejected promises with no handlers...)
To make sure we're referring to the same GC technique, I am referring to doing a MakeWeak on the promise and using the callback to log. I imagined that would be fast because Node code seems to do it pretty often, but I could be wrong.
#11: yes, MakeWeak is quite slow (this was one of the motivations for typed array rewrite as well).
Cc: amikhaylova@google.com
Blocking: chromium:348919
Owner: yangguo@chromium.org
Status: Assigned
Under review: https://codereview.chromium.org/249503002/
The idea is to provide devtools with a debug event in case an exception is thrown in a promise and we don't have a reject handler for it yet.
(Disclaimer: I work on the JavaScript tools for Microsoft like the F12 tools and Visual Studio debugger.)

We had a similar problem with promises in WinJS wherein catching exceptions in order to trigger an error handler meant that no-end of developers were not breaking or even noticing they had an issue. Basically to them the exception became silent and swallowed. We looked at mitigating this in a few ways:
1) By enabling first chance exceptions (break on all exceptions). This was bad as no one does that as you break all over the place.
2) By logging an error to the console.  This was bad as few people noticed it (at least out of the ~10 UX studies we did observing participants in a lab) and when they did they wanted more info or to break. 
3) By dumping the stashed exceptions on some event (GC, nav, anything) all of which suffered from #2 but were even more obscure as the relation in time wasn't clear to developers.
4) By encouraging developers to use .done() rather than .then(). As .done throws .then doesn't. This suffered because we made the change late in the cycle after a pattern of using .then had been established. Then, worse, for new code there was no good way to establish the use of .done over .then

In the end for VS 2012 we settled on exposing an API to the page (Debug.setNonUserCodeExceptions) that couple be used by the debugger when calculating if an exception is unhandled. WinJS was then instrumented with this so at the end of the day WinJS customers would break in their code in a promised when break on unhandled exception was turned on (the default). That pretty much nailed the problem for us. Ultimately though we wanted to use Just My Code for this feature wherein any frames on the stack that had catch handlers would be checked if they were in library code and if they were then they wouldn't count towards an exception being treated as 'caught'.
WinJS use of the property: https://github.com/winjs/winjs/search?q=Debug.setNonUserCodeExceptions&ref=cmdform 
MSDN documentation: http://msdn.microsoft.com/en-us/library/ie/hh974372(v=vs.94).aspx 

This is great insight, thanks!
Going forward though in VS and F12 we're really relying on the Just My Code feature so that developers mark files as library and thus don't need to rely on instrumentation in a library. The property setNonUserCodeExceptions really was meant as a bandaid till we had time to implement JMC as we basically didn't want to ship VS without doing something to make promise debugging manageable. 
In any case, if an exception is caught by a Promise and at the point of throw it has no reject handler, we will consider it an uncaught exception. So using "pause on uncaught exception" in devtools will hit a break.

https://developers.google.com/chrome-developer-tools/docs/javascript-debugging#pause-on-uncaught-exceptions
@19, That sounds wrong. Imagine the following example:

var p = new Promise(function(resolve, reject) { reject(new Error('Something went wrong'); });
p.then(function(result) {
  console.log('Promise success', result);
}, function(error) {
  console.error('Promise failed', error);
});

After the first line the promise's rejection is effectively unhandled. There is however no way for the user to handle the rejection earlier and thus avoid the temporary unhandled state.
@20, the reject function just performs an Enqueue Task (see https://people.mozilla.org/~jorendorff/es6-draft.html#sec-rejectpromise) and doesn't immediately reject.  So the handlers will be attached before the rejection is handled.

Besides, as I understand it, the result will be that the debugger will pause 'as if it is uncaught' -- that presumably doesn't prevent the developer from hitting continue and letting a later handler catch the exception.  So the worse case would be a false positive (and your example isn't even that).
Right, but for the dev tools to interrupt at the time the promise is rejected, the check for unhandled would have to be done at Enqueue time.

As a developer, if the dev tools interrupted me every time a promise is rejected but actually handled, I would quickly disable the "stop on uncaught". As Domenic explained before, it would be a disincentive for developer to use promises.
Not sure if I misunderstand, but
- If a promise is rejected not through throw, but via calling the reject function, nothing happens.
- At the throw site, we look for the promise that will get rejected as consequence. If that promise has no reject handler, we consider that an uncaught exception.
- If at that point the promise already has a reject handler, nothing happens.
- Pause on uncaught exception is not even on by default. Nothing happens if you don't care about uncaught exceptions.

I suggest you try the latest Canary.

Comment 24 by m.go...@gmail.com, May 9 2014

Re #23:
"- If a promise is rejected not through throw, but via calling the reject function, nothing happens."
Why? Shouldn't it behave in the same way as when you throw and not have an error handler attached by then?
Let me try to be clear: none of the behavior here is part of any specification. It's merely to help developers. The issue here is that Promises swallow exceptions, and the developer may not have intended that and would like to figure out if exceptions were indeed thrown. That's not the case if he already attached a reject handler. If he calls the reject function, well, then there is no exception involved at all.
Sorry I hadn't realized throw and rejections were handled differently. This is actually counter intuitive when using promises. I would argue the dev tools should handle the two similarly.

Base on this information I wrote a slightly more "realistic" example: http://jsfiddle.net/mhofman/gr9nA/

In Canary, when pausing on uncaught exceptions, the debugger stops, which I believe shouldn't be the case.
Yes, I would strongly suggest treating reject and throw exactly the same. In both cases an exception is involved; in one case the exception is communicated via the language's built-in `throw` keyword, and in others the exception is communicated via the language's built-in `reject` function.

After playing around with the current behavior I agree with mathieu's concerns that the experience is pretty frustrating and counteruintuive. His example is a good one.
Forgot to mention: the Canary experience is basically the same as the "blue" error stop sign (in the old UI when there was a blue/purple split, i.e. it is akin to checking "Pause on Caught Exceptions" in the new UI). As we all know that can occasionally be useful, but is often just frustrating and not worth turning on.

What would be preferable is something more like the "purple" error stop sign, which waits until at least the end of the turn to see if the rejection becomes handled.
To summarise what I think Domenic is saying:

"Break on uncaught exception" - Break when a promise without a reject handler is rejected. This includes rejects triggered with throw & calls to reject() in Promise constructors.

"Break on caught exceptions" - Break when a promise is rejected.

I understand the issue in #20, but I'm not sure what the better option is for synchronous calls to reject in promise constructors.

The case I'm less sure about is:

Promise.resolve().then(function() {
  return Promise.reject();
});

Does Promise.reject() count as an uncaught exception? Is Promise.reject().catch(function(){}) also an uncaught exception?
Jake: what libraries like Bluebird do is wait until the end of the turn to make any decisions about unhandled or not. So in your particular examples:

> var p = new Promise(function(resolve, reject) { reject(new Error('Something went wrong'); });
> p.then(function(result) {
>   console.log('Promise success', result);
> }, function(error) {
>  console.error('Promise failed', error);
> });

Handled, since by the end of the turn an error handler is attached (and the return value of p.then is fulfilled).

> var p = Promise.resolve();
> var q = p.then(function onFulfilled() {
>  var r = Promise.reject();
>  return r;
> });

Handled, since during the turn (a) p is fulfilled and so a microtask is queued to run onFulfilled; (b) onFulfilled runs and creates r, which is for-now unhandled; (c) onFulfilled returns, and the algorithm that called onFulfilled takes over, queueing a microtask to run r.then(resolveQ, rejectQ); (d) said microtask happens, making r now handled, and resolving q; (e) turn finally ends, and in we survey and find that p is fulfilled, q is fulfilled, and r is rejected but handled.

> Promise.reject().catch(function(){})

Same as the first case, but with less typing.
Yeah, that would work fine in terms of logging errors, but doesn't make sense for "break on error".
I'm not sure. From what I can tell the debugger already has some ability to "rewind the stack" and break at the point an exception was thrown, even after the exception has propagated all the way to the top level. That is, when you do "break on uncaught exception" and throw a sync exception nested inside many function contexts, it still breaks at the line the exception was thrown, instead of breaking inside e.g. the window.onerror implementation.

Plus, of course, there's the entire "temporal debugging" aspect of http://www.html5rocks.com/en/tutorials/developertools/async-call-stack/.

So there is already some ability to break at the correct point, despite the error having been thrown "in the past."
Cc: rponnada@chromium.org yhirano@chromium.org
 Issue chromium:380693  has been merged into this issue.
Cc: abarth@chromium.org esprehn@chromium.org
 Issue chromium:387505  has been merged into this issue.
What's the status of this? Using Promises is incredibly painful right now.

Comment 36 by ojan@chromium.org, Jun 23 2014

Finding a way where we log to the console if there's no rejection handler is crucial. I understand that there are use-cases where you might not want that, but the current state of silently failing when there's a JS error is unusable.
It's fine to log if there's an unhandled rejection, as long as you *unlog* it once the rejection becomes handled. Otherwise the console becomes useless for entire classes of promise-using webapps.

Comment 38 by ojan@chromium.org, Jun 24 2014

Unlogging sounds really complicated to me. The use-case of explicitly wanting to add rejections after the fact seems really niche to me. We should support it, but it should not be the default. You should have to opt-in to it (e.g. via a release() method on the promise).

The default behavior should be that if a promise has an unhandled rejection when we hit the event loop it should:
1. Log to the console
2. Fire window.onerror

Promises are unusable because of this. Seriously. This is terrible. I'm embarrassed we shipped this. I'm tempted to say we should unship. It's really that bad.
>  The use-case of explicitly wanting to add rejections after the fact seems really niche to me.

Let me assure you that, having written many, many promise-using applications, it is not niche and shows up in any nontrivial asynchronous code.

> Unlogging sounds really complicated to me.

Does devtools really have no ability to modify the console, after it has been written to?

Comment 40 by ojan@chromium.org, Jun 24 2014

Unlogging just sounds like a bad user experience to me. If you have the console open you'd see the error flicker by.

But, even that aside, a pure-devtools based solution is insufficient. It's a deal-breaker if apps need to add rejection handlers to all their promises in order to be able to capture JS errors to log back to the server. window.onerror needs to work by default. The window.onerror-breaking mode needs to be opt-in.

> >  The use-case of explicitly wanting to add rejections after the fact seems really niche to me.
> Let me assure you that, having written many, many promise-using applications, it is not niche and shows up in any nontrivial asynchronous code.

Depends on your definition of niche I suppose. I think it's *much* less common than wanting to get errors messages when you typo.

What are you suggesting as the solution? Do you think having the devtools break on exception thing work better is actually sufficient? I don't.

Oh, and I forgot to add: I understand your frustration. I think shipping promises as-is is equivalent to shipping a JS engine without a debugger or any indication that script errors of any sort occurred. They are indeed incomplete, by modern standards of developer tooling.

What you are proposing would move us into the analogy of the Netscape 3 days, where script errors would show up as alerts that the developer has to dismiss, one by one, even if the errors are wrapped in try/catch.

It seems better to bypass that medieval era, and simply add a proper promise debugger. One already exists as a Chrome extension for Ember's promises, for example. We're not breaking new ground here; the UI and UX are well-understood and well-charted.

In the meantime, before such Chrome extensions existed, people have debugged promises using a variety of techniques, none of which were optimal, but all of which alleviated their frustration. Promise users are used to this. The situation is not unship-level drastic.

----

Ah, a response came in as I was typing.

> Unlogging just sounds like a bad user experience to me. If you have the console open you'd see the error flicker by.

Assuming you batched up changes to devtools with each microtask, you would see no flicker unless the error was actually handled asynchronously, which you claim is rare. (But again, in my experience it is common, especially in UI-based code; often errors will not be handled until a user action, for example.)

> It's a deal-breaker if apps need to add rejection handlers to all their promises in order to be able to capture JS errors to log back to the server. window.onerror needs to work by default.

`window.onerror` is certainly not the right tool here, as in production promise-using code such unhandled rejections will not be forever-unhandled, but instead temporarily unhandled. A `window.onpossiblyunhandledrejection`, preferably coupled with a `window.onrejectionhandled`, would be much more accurate, and is what most promise libraries provide. (Bikeshed as you please.)

Also remember that "all their promises" is not actually accurate; it's "all their promise chains," of which there is generally only one per asynchronous entry point into the application (i.e. main loop, event handler, etc.). This is what makes strategies such as .done(), while sucky, also feasible.

> What are you suggesting as the solution? Do you think having the devtools break on exception thing work better is actually sufficient? I don't.

A proper promise debugger, as in the Ember inspector, is what you really need to be able to see the state of promises in your system. This includes not just spurious unhandled rejections, but also forever-pending promises, unhandled fulfillments, the flow of data between asynchronous entry points along various chains, and so on. Unhandled rejections are the most painful bit, and so if you want a quick patch to fix that frustration, a temporary log entry that can be unlogged after handling is sufficient for that case. But the rest are just as important. Soon enough you will be claiming promises are unusable when you can't see your forever-pending promises.

I think break-on-rejection, if properly implemented (with modes for both handled and unhandled-by-end-of-turn), would be a good thing, although it would be roughly as useful as "break on all exceptions" given the number of spurious false positives it would catch. And it's very easy to get wrong: there's proposals upthread to make it only work for thrown errors, for example, which would completely fail to catch other rejections (e.g. failed database queries). But regardless, the actual primitive at hand is a live list of promise rejections, and you need to give some insight into that.

For example, onunhandledrejection/onrejectionhandled helps reify this list, by letting you send follow-up messages to your server saying that the previously-reported rejection was actually handled after all. In effect, this syncs the live list between client and server, emphasizing how the live list is the actual primitive.

So I guess, if nothing else, my point is this: don't just focus on one half of the live list. An ever-growing list of failures is not an accurate reflection of the promise flow of a JS app.
Wow. Huge thread over night.

So here's the current state:

When an exception is thrown in a promise, it would be swallowed and forwarded to the (possibly not yet) registered reject handlers. If the debugger is active (devtools opened), the debug event listener (registered by devtools) receives an uncaught exception event if there is no reject handler registered at the time of the throw. If you turned on "break on uncaught exception", this stops execution.

A problem with this is that if the exception is thrown in the closure passed to the Promise constructor, there can't be possibly a reject handler registered yet, so we always get that uncaught exception even if we register a reject handler later for the end of turn.

What also exist are PromiseMirror objects to be used by devtools. So far they only offer insight into the state of the PromiseMirror and its value when resolved/rejected. I don't think this is already used by devtools yet at the moment.

The next step is to introduce a new debug event type for Promise events, such as chaining, resolving/rejecting etc. Using those events, devtools could figure out the dependency of Promise chains to display. We do not want to explicitly keep those dependencies in V8 to prevent memory leaks.
What Yang says, and the devtools team is working on it.

As for "unlogging", that seems like an oxymoron to me. AFAIAC a log is a monotonically growing history of events, not something mutable. I agree that it would be a bad and confusing user experience to violate this expectation and rewrite history.
Breakpoint debugging relies on, well, breakpointing.  Thus any solution to this fundamental problem of promises within a breakpoint debugger has to result in the developer stopped on a breakpoint at the point where the uncaught exception is thrown.  How could we get there?

As discussed here, breaking on uncaught exception is impractical because the old technique of marking an exception as "uncaught" relies on examining the call stack for catch handlers. In the case of promises, the exception is very likely to be in a different call stack from potential handlers.   Breaking on all exceptions leads to too many false breaks.

Support for conditional exception breakpoints would make this situation and several others much less painful. The idea is to allow developers and/or libraries to indicate which exceptions are known to be handled correctly by the program so that breaking on any other exception will likely be valuable to the developer.   One approach is discussed on the Chrome devtools issue list:
https://code.google.com/p/chromium/issues/detail?id=108779
Coupled with a JustMyCode-like filter, this approach could improve Yang's effort. Then it could be extended to allow conditional breakpoints based on Promise dependency chain analysis. This is much more difficult because it must be done across executions.  (I'm skeptical that simply displaying promise chain info will be helpful for developers since if the buy into Promises they will have lots of them.)

Comment 45 by ojan@chromium.org, Jun 24 2014

I'm glad devtools work is being done here, but I still don't think that's sufficient. We need platform-exposed changes here as well. Web sites need to be able to log stack traces back to the server.

Domenic, can you give an example of an async case where this matters and why an opt-in release-style API would not work for that? The solutions that you're proposing seem unecessarily complex to me, but maybe I'm just not thinking of the use-cases properly. 

Also, is there a better place to be discussing this than on a V8 bug? I'm not familiar with where the spec work for this is happening.
Any case where you prefetch data often decouples the handling of a rejection from the time the promise was rejected, since you only `.then(...)` the promise when you want to use the data.

Any case where you use promises to signal state transitions also often falls into this pattern. For example a font face may fail to load, or a stream may fail to close, but you only use their `.loaded` or `.closed` promises when you actually need to react to those situations, e.g. after the text you wish to render in that font has been received from the server, or after the destination you're writing the stream's data to signals that it is ready to accept more data.

These kind of situations are especially common in UI programming, where you may be performing some operation asynchronously, but only need to know about its failure when the user clicks "show me the results" or "next page" or "print" or similar. Indeed, in those cases there's often nowhere to sensibly display the error until the user action takes place!

For example, in an eBook reading application, we had promises representing the next and previous page renderings, which were kicked off ahead of time. When the user clicked the next page, we would do the page transition animation, clearing out the displayed page. Only at this point would we `.then()` the next-page promise, rendering the contents into the display area on fulfillment, and rendering an error message into the display area on rejection. This could not have been done earlier, because the display area was filled with the stuff the user was already reading!

In all cases, you can contort your code to avoid delayed-thening, essentially by immediate-thening and then storing the results in a placeholder variable that you later poll the value of. But one of the main benefits of promises is to be able to represent latent asynchronous results in this way, without such placeholder variables! You're essentially back in callback land if you lose that ability.
Nit: no need for a contortion, really: since you can chain multiple handlers to one promise, the simpler way would be to keep the late chaining as you desire, but just also immediately chain a (dummy) error handler.

Not saying this is a desirable solution...
Blocking: chromium:393913

Comment 49 by ojan@chromium.org, Aug 7 2014

I still think we're killing the 99% use-case for the 1% use-case. We should take this discussion to the appropriate standards body. Where is this being discussed/specced?

So, as described in Chromium issue 393913, with r22913, rejecting Promises triggers an exception debug event just like throwing in Promises. Just like throwing, they can be caught or uncaught depending on whether there are rejection handlers.

At this point, I don't think we need a option for "Break on Rejection" to distinguish between rejects and throws. Do we?

Regarding specification: I think devtools/debugger behavior is not in the scope of any standard.
The following will neither stop on uncaught exceptions, not print anything to DevTools console.

var resolve, reject;
var p = new Promise(function(_resolve, _reject) { resolve = _resolve; reject = _reject; });
p.then(function() {
    console.log("p.then 1");
    throw new Error("123");
}).then(function() {
    console.log("p.then 2");
});
setTimeout(resolve, 0);

In the example #51, the second "then" gives a false impression of catching exceptions from the previous promise. We should check that the promiseOnReject handler is registered, instead of checking (promiseOnResolve || promiseOnReject).

If the second then is dropped, this works as intended.

Comment 53 Deleted

Labels: -Priority-Medium Priority-High
@yangguo,
DevTools no longer prints exception messages upon promise exceptions. This seems to be a regression after your recent changes.

Repro:

var resolve, reject;
var p = new Promise(function(_resolve, _reject) { resolve = _resolve; reject = _reject; });
p.then(function() {
    console.log("p.then 1");
    throw new Error("123");
});
setTimeout(resolve, 0);
To document what we discussed:
#54 would be a feature request. Rejections are only treated the same as throws wrt exception debug events. Wrt Javascript execution, they are different: an uncaught throw interrupts execution, and a message is posted to Chrome, which logs to console, even if devtools is off. An uncaught** rejection/throw in a Promise doesn't interrupt execution, V8 carries on, since that's what's expected. There is no pending exception and no pending message, hence there is no message logged to console.

** uncaught in the sense that there is no rejection handler.
#51 works fine in V8, with a small twist:
the first then actually adds a default reject handler, as it's not provided as second argument. That default reject handler simply passes the rejection to the chained promise, which eventually triggers an uncaught exception debug event caused by rejection. Only that event happens when the default reject handler passes the rejection on. That is in the internal JS implementation, and the stack frame is not exposed. With no stack frame at all, the debug event is ignored by devtools.
Project Member

Comment 57 by bugdroid1@chromium.org, Aug 14 2014

The following revision refers to this bug:
  https://chromium.googlesource.com/external/v8.git/+/0ae0b81cdbadf5ad0448a1871df0737e38eabf02

commit 0ae0b81cdbadf5ad0448a1871df0737e38eabf02
Author: yangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Date: Thu Aug 14 06:57:48 2014

Ignore default reject handler when looking for reject handlers.

LOG=N
BUG= v8:3093 
R=aandrey@chromium.org

Review URL: https://codereview.chromium.org/461023002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23112 ce2b1a6d-e550-0410-aec6-3dcde31c8c00


> Ignore default reject handler when looking for reject handlers.

I don't know exactly what implementation strategy you guys are pursuing, so I could be wrong, but I don't think this is what is wanted by either the telemetry solution or the devtools solution. Default reject handlers should count toward being handled. That is, in

var p = Promise.reject(new Error("foo"));
var q = p.then(function () { });

Only (q, [Error: foo]) should be in the unhandled-rejections list; (p, [Error: foo]) should not be in the list.
@#58:

Default reject handlers should count as toward being handled, I agree. But, consider this:

var p = new Promise() {
  throw new Error();
}

var q = p.then(function() {});


p is rejected, but is handled by the default reject handler in q. q is rejected through that default reject handler, but does not have any reject handler. So devtools breaks there, which is in the middle of the default reject handler. The stack trace is no helpful, since there is none outside of the internal promise implementation. It would make more sense for the developer to have the break at the throw site.

That's why the patch changes the promise-has-reject-handler check to do a tree search for non-default reject handlers. If there are only ever default reject handlers in the tree, the reject is eventually going to become uncaught. In that case, it makes sense to get devtools to break as early as possible, at the throw site.
@#59:

OK, I guess I was focused too much on the displaying-unhandled-rejections feature, and not on the when-to-break feature. Sounds like you have things well in hand, and thanks for explaining :)
Cc: yu...@chromium.org
With my CL to report unhandled rejected promises [0] results in something shown in the attached screenshot.

Obviously we still need to fix up things on the Blink side to keep those reported messages until the end of turn, recheck the promise on whether still unhandled, and then output on the console.

[0] https://codereview.chromium.org/607913002/ 
Screenshot from 2014-09-26 16:56:17.png
74.3 KB View Download
Project Member

Comment 62 by bugdroid1@chromium.org, Sep 30 2014

The following revision refers to this bug:
  https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0

commit e68e62c8917405e33155965303bb263fae68fcb0
Author: yangguo@chromium.org <yangguo@chromium.org>
Date: Tue Sep 30 15:29:08 2014

Introduce PromiseRejectCallback.

R=aandrey@chromium.org, yurys@chromium.org, rossberg@chromium.org
API=v8::Isolate::SetPromiseRejectCallback, v8::Promise::HasHandler
LOG=Y
BUG= v8:3093 

Review URL: https://codereview.chromium.org/600723005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24335 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/include/v8.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/api.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/api.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/bootstrapper.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/compiler/linkage.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/contexts.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/debug.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/debug.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/heap/heap.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/heap/heap.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/isolate.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/isolate.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/promise.js
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/runtime/runtime.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/src/runtime/runtime.h
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/test/cctest/test-api.cc
[modify] https://chromium.googlesource.com/v8/v8.git/+/e68e62c8917405e33155965303bb263fae68fcb0/test/cctest/test-debug.cc

Status: Fixed
Thanks so much for fixing this, it's made developers much happier.

Comment 65 by mi...@zoltu.net, Jun 20 2015

How do I disable this?  My logs are full of these errors even though they aren't errors because I create some promises in a rejected state intentionally (so future reads can access the rejection).

Note: Unhandled promise rejection is intentionally not in the spec because it breaks some promise use cases so this should be an opt-in feature.  However, I will settle for opt-out at the moment.
Just add a no-op catch handler.

function silentReject(error) {
  var result = Promise.reject(error);
  result.catch(function() { });
  return result;
}

Blocking: chromium:348919
I believe there is a bug in the way promises are checked for unhandled.
A rejection handler added through 'promise.catch' in the same turn can still trigger the 'onunhandledrejection' event and outputs an error on the console.

I filed a bug on Chromium since I'm not sure if it's a V8 bug or not:
https://bugs.chromium.org/p/chromium/issues/detail?id=614241
As explained in crbug/614241, this is not a bug, but working as intended. Promise.then does not cause a promise to be considered handled if you don't pass an explicit reject handler.
Project Member

Comment 70 by bugdroid1@chromium.org, Sep 20 2016

The following revision refers to this bug:
  https://chromium.googlesource.com/v8/v8.git/+/377358516fe422dacc26614bf94c8ad54e6cbaf2

commit 377358516fe422dacc26614bf94c8ad54e6cbaf2
Author: littledan <littledan@chromium.org>
Date: Tue Sep 20 19:33:39 2016

Make Promise.all/Promise.race catch prediction conditional on DevTools

To improve performance, this patch makes Promise.all and Promise.race not
perform correct catch prediction when the debugger is not open. The case
may come up if Promise.race or Promise.all is called, then DevTools is
open, then a component Promise is rejected. In this case, the user would
falsely get an exception event even if the "pause on caught exceptions"
box is unchecked. There are tests which triggered this case; however, it
seems both unlikely and and acceptable to have an event in this case.
Many analogous events are already produced when DevTools is enabled
during the operation of a program.

BUG= v8:3093 

Review-Url: https://codereview.chromium.org/2350363002
Cr-Commit-Position: refs/heads/master@{#39565}

[modify] https://crrev.com/377358516fe422dacc26614bf94c8ad54e6cbaf2/src/js/promise.js
[modify] https://crrev.com/377358516fe422dacc26614bf94c8ad54e6cbaf2/test/mjsunit/es6/debug-promises/promise-all-uncaught.js
[modify] https://crrev.com/377358516fe422dacc26614bf94c8ad54e6cbaf2/test/mjsunit/es6/debug-promises/promise-race-uncaught.js

Labels: Priority-1

Sign in to add a comment