Skip to content

Conversation

@mgol
Copy link
Member

@mgol mgol commented Jun 29, 2016

Summary

Re-throw errors that happened in callbacks wrapped in jQuery ready

Also, expose jQuery.readyException that allows to overwrite the default
ready error handler.

Checklist

Mark an [x] for completed items, if you're not sure leave them unchecked and we can assist.

Thanks! Bots and humans will be around shortly to check it out.

Fixes gh-3174

@mgol
Copy link
Member Author

mgol commented Jun 29, 2016

Please review. I'd like to have it released quickly, preferably no later than next week. cc @jquery/core

@mgol mgol removed this from the 3.1.0 milestone Jun 29, 2016
var message,
origWindowSetTimeout = window.setTimeout;

window.setTimeout = function( fn ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering how you'd do this 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use sinon here, right?

Copy link
Member Author

@mgol mgol Jul 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was thinking about sinon but it's not activated at all for this module, I'd need to enter the sinon sandbox to be able to use spies, wouldn't I?

Copy link
Member

@markelog markelog Jul 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary to use sandbox i think, sandbox usually needed to clean up some scope of spies/stubs, you probably could just use -

sinon.stub(window, 'setTimeout', myOwnThing);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I'll try that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use sinon if you want, but for a case like this it's really nice to be able to look at the code and understand what it does without reading though sinon docs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say sinon is used almost universally for mocking/spying/stubing/etc it has very simple obvious api with clear documentation and we already use it.

So i would suggest to use it anywhere we can

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@markelog I had to create a sandbox and use this.sandbox.stub instead of sinon.stub or stubs wouldn't have been cleaned up (because how would Sinon know when to clean up?). It works fine now.

@dmethvin
Copy link
Member

dmethvin commented Jul 1, 2016

LGTM

define( [
"../core",
"../var/document",
"../core/readyException",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 3.1 only though i.e. minor? Do we have corresponding docs pr/issue?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you labelled it as 3.1.0, just checking

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, i see jquery/api.jquery.com#942, we need a pr for it though? Before we can release?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll submit a docs PR once this is accepted, no worries.

At the meeting we agreed that since this issue kind of breaks all Angular apps (as they wrap their init code in $() and all bootstrapping errors are silenced) we'll release a quick 3.1.0 with maybe only this fix. I renamed previous milestones: 3.0.1 to 3.1.1 and 3.1.0 to 3.2.0.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

@markelog
Copy link
Member

markelog commented Jul 1, 2016

Do you wanna release before providing a fix for #3194?

jQuery.readyException = function( error ) {
window.setTimeout( function() {
throw error;
} );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot @gibson042 wanted to have this reject as well so that a more general potential unhandled rejections mechanism would catch it. The code would be:

jQuery.readyException = function( error ) {
    window.setTimeout( function() {
        throw error;
    } );
    throw error;
};

I have doubts about the need for that, though - the error thrown in a timeout would fire window.onerror anyway, do we need both mechanism to work here? Usually an error is either synchronous and handled by window.onerror or asynchronous and handled by the other mechanism.

cc @dmethvin as you already LGTM'd this PR. :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems we would need to rename readyException? If it would be used in more general sense? Also we need to re-throw error twice?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not meant to be used in a more general sense, it was only about integrating with existing debugging tools.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if i understand the use-case, maybe it would be better to discuss this in separate ticket?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot @gibson042 wanted to have this reject as well so that a more general potential unhandled rejections mechanism would catch it.

If you wanted to catch your own ready handler's rejections you can already do $.when($.ready).then(fn).catch(errfn) in 3.0. What other cases do we want to handle? I feel like 99.9% of the cases are errors and as long as we manage to dump a stack we're doing all we need to do.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The important detail to me is that our default handling doesn't suppress unhandled rejections, since doing so was the essence of the original complaint (and I still want to keep the door open for an unhandled rejections plugin). In fact, if we were willing to advocate $.when($.ready).then(fn).catch(errfn) strongly enough, then gh-3174 could be closed with no code changes whatsoever.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gibson042 I think that could work only if we deprecated/removed $(fn) but this is such a popular pattern that I don't see it going away any time soon. And this fact alone is enough for me to want it not silencing exceptions.

@nicklou
Copy link

nicklou commented Jul 5, 2016

Is it possible that we use something similar to$.on( "error", handler ) method, but with a different event?

For instance:

$( fn ).on( "exception", handler );

That way, you don't break the existing API and the modification is not on deferreds necessarily, although I am not sure if it is possible to put a correlation between the fn called and the .on() handler.

@dmethvin
Copy link
Member

dmethvin commented Jul 5, 2016

For existing code, no additional API is useful unless its default provides helpful information, which this PR does. Someone who doesn't like the default can change it.

For new code where an exception is expected, you can either attach a handler to the promise via .catch() as described above or you can just add a try/catch to the synchronous code.

I don't think anything else is required is it? What case would another API handle that those options do not?

@mgol mgol force-pushed the readyException branch from 682c5e4 to 0fe6cad Compare July 6, 2016 09:06
mgol added a commit to mgol/jquery that referenced this pull request Jul 6, 2016
Also, expose jQuery.readyException that allows to overwrite the default
ready error handler.

Fixes jquerygh-3174
Closes jquerygh-3210
@mgol
Copy link
Member Author

mgol commented Jul 6, 2016

The PR is ready and passes npm test locally. Travis fails because npm currently has problems in some locations.

I've also submitted API docs for this method: jquery/api.jquery.com#946. Please review.

I'd like to land this today if no one objects.

jQuery( function() {
throw new Error( "Error in jQuery ready" );
} );
assert.strictEqual( message, "Error in jQuery ready", "The error wasn't thrown in a timeout" );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't "wasn't" here be "was"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@gibson042
Copy link
Member

The default jQuery.readyException still needs a throw error.

@mgol mgol force-pushed the readyException branch from 0fe6cad to bfbc395 Compare July 6, 2016 18:41
mgol added a commit to mgol/jquery that referenced this pull request Jul 6, 2016
Also, expose jQuery.readyException that allows to overwrite the default
ready error handler.

Fixes jquerygh-3174
Closes jquerygh-3210
@mgol
Copy link
Member Author

mgol commented Jul 6, 2016

The default jQuery.readyException still needs a throw error.

What about the @dmethvin's remark at #3210 (comment)?

I'm not sure I see the need of a double-error-dispatch approach here. In general there are two kinds of errors - the ones that propagate to window.onerror and the ones who don't and might be collected by a mechanism that records unhandled promise rejections (https://github.com/jquery/jquery/blob/3.0.0/src/deferred/exceptionHook.js). If here we'd throw both synchronously and asynchronously, we'd collect the error in both of them.

To me the fact that jQuery( fn ) uses Deferreds is somewhat an implementation details since you can't use this API in the same way as you can attach error handlers to promises (the jQuery.ready promise is meant for that).

@timmywil
Copy link
Member

timmywil commented Jul 6, 2016

I agree. The main problem was that there was no info on the error. One throw seems like enough.

@gibson042
Copy link
Member

To me the fact that jQuery( fn ) uses Deferreds is somewhat an implementation details since you can't use this API in the same way as you can attach error handlers to promises (the jQuery.ready promise is meant for that).

Okay, that's persuasive. Suggestion withdrawn.

readyList.then( fn );
readyList
.then( fn )
.catch( function( error ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.catch(jQuery.readyException)

wouldn't work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment there.

Also, expose jQuery.readyException that allows to overwrite the default
ready error handler.

Fixes jquerygh-3174
Closes jquerygh-3210
@mgol mgol force-pushed the readyException branch from bfbc395 to ad6a94c Compare July 7, 2016 08:23
@markelog
Copy link
Member

markelog commented Jul 7, 2016

LGTM

@mgol
Copy link
Member Author

mgol commented Jul 7, 2016

All comments addressed. I'll land it later today unless new review remarks come up before.

@mgol mgol merged commit ad6a94c into jquery:master Jul 7, 2016
@mgol mgol deleted the readyException branch July 7, 2016 16:07
@lock lock bot locked as resolved and limited conversation to collaborators Jan 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Exceptions silently swallowed in $(fn)

8 participants