Side navigation
#8744 closed bug (cantfix)
Opened April 01, 2011 04:46PM UTC
Closed January 26, 2013 09:35PM UTC
Last modified April 01, 2013 03:51AM UTC
.ajax() jsonp requests are not handled correctly when hitting timeout
Reported by: | Dynalon | Owned by: | jaubourg |
---|---|---|---|
Priority: | high | Milestone: | 1.next |
Component: | ajax | Version: | 1.5.2 |
Keywords: | jsonp timeout | Cc: | jaubourg |
Blocked by: | Blocking: |
Description
The $.ajax() call does not handle cross-domain JSONP requests correctly if the request fails because of a timeout.
As soon as the timeout was reached, the corresponding callbacks error/complete/success are fired correctly. However, the xhr is not canceled and after response (even if its too late now) jQuery jumps into the callback handler of the jsonp, even so the function does not exists (anymore). You will hit an error like "jQuery152008986116735648775_1301675055297 is not a function" in your error console.
(quite) minimal testcase which will always timeout:
function doTest() { $.ajax({ url: 'http://jsfiddle.net/echo/jsonp/', timeout: 5, dataType: "jsonp", data: { echo: "Hello World!" }, error: function(jqxhr, textStatus) { console.log("failed with error: " + textStatus); }, success: function(data, jqxhr, textStatus) { alert(data.echo); } }); } $("document").ready(doTest); // attention: you cannot run that testcase on jsFiddle because // it will then not be a cross-domain request!
If you set the timeout in the code above to a reasonable timeframe, i.e. 5000, the cross-domain request will finish properly.
Expected behaviour: jQuery should correctly cancel the XHR when hitting the timeout.
Tested Browser: Firefox 4 and Opera 11 are both affected, Safari 5 however seems not to be (all tested on Mac).
Attachments (0)
Change History (38)
Changed April 02, 2011 05:17PM UTC by comment:1
cc: | → jaubourg |
---|---|
component: | unfiled → ajax |
keywords: | → jsonp timeout |
Changed April 02, 2011 08:02PM UTC by comment:2
I've digged into this a little more and yes, it seems the problem is not easily solveable. The JSONP request is done via injection of a <script> tag into the DOM and I don't think (?) there is a way to cancel the request once its triggered. The problem is, the callback function is removed from the window context by jquery right after hitting the timeout. If the request does finish later, the jsonp response is run by the browser as a standalone-script and thus first hits an undefined function, causing the "unknown function" exception.
However, it turns out that this bug is a really minor one. As the exception is thrown in the standalone script of the jsonp response, it should not affect any other scripts running, causing no damage at all except for the error messge in the error console.
I've come up with a simple hack in jquery so that the callback function is not cleaned up and remains within the window object. This resolves the problem but is surely no feasible solution for upstream since the window object would be cluttered with (afterwards useless) functions over time when doing lots of jsonp requests.
To have this completeley fixed a better way for callback handling would be needed, but considering that the bug itself doesn't raise serious issues I think its not worth the effort.
Changed April 02, 2011 11:03PM UTC by comment:3
_comment0: | I find this behaviour very strange. I'm not on my dev box right now but afaik, jqXHR.abort (which is used by timeout) will remove the script tag from the node... so is it that FF4 and Opera 11 don't cancel script loading in that case or is something wrong in the script tag removal code? \ \ I'll look into this on my dev box once I'm back on it. → 1301785434593249 |
---|---|
owner: | → jaubourg |
status: | new → assigned |
I find this behaviour very strange. I'm not on my dev box right now but afaik, jqXHR.abort (which is used by timeout) will remove the script tag from the document... so is it that FF4 and Opera 11 don't cancel script loading in that case or is something wrong in the script tag removal code?
I'll look into this on my dev box once I'm back on it.
Changed April 04, 2011 03:41PM UTC by comment:4
resolution: | → fixed |
---|---|
status: | assigned → closed |
Fixes #8744. Makes sure script transport abort method actually removes the script tag even if readyState exists.
Changeset: 2ed81b44be958b5f2b5569ab15f22bde262b4eb6
Changed April 04, 2011 03:42PM UTC by comment:5
Dynalon, can you confirm this fixes your issue?
Changed April 04, 2011 03:51PM UTC by comment:6
milestone: | 1.next → 1.6 |
---|
Changed April 04, 2011 04:15PM UTC by comment:7
Yes, I confirm with the recent git version the problem is gone, bug is fixed. Thanks!
Changed April 04, 2011 04:26PM UTC by comment:8
Stop, that one was to quick: The Bugfix in Git works for Opera 11 (Mac & Linux tested), but is still present in Firefox 3.6.13 (Linux) and Firefox 4 (Mac). So the Fix does not seem to work on Firefox at all.
Changed April 04, 2011 07:05PM UTC by comment:9
I think jaubourg had the right idea about firefox script loading. Once the script tag is injected, I believe it loads the script regardless of whether the tag gets removed before completion. Another thought I had was that I think async is set to true by default for injected script tags in Firefox 4. I'm not sure if that could have an impact on removals. Again, I defer to jaubourg on these issues.
Changed April 04, 2011 11:36PM UTC by comment:10
Dynalon: could you set a test case on jsFiddle (calling the twitter public api or whatnot). I'd like to see this with my own eyes because, if that's the case and what timmywil says is right then it's clearly a bug in Firefox. I highly doubt it though, so I'll need a test case clearly showing the behaviour to re-open this (are you sure you didn't have some caching issues when you tested in FF?).
Changed April 05, 2011 12:06AM UTC by comment:11
_comment0: | Yes, actually it seems if you enforce dataType:jsonp as ajax option, the bug appears not matter if cross-domain or not. So I now have set up a testcase on jsFiddle: \ \ http://jsfiddle.net/XtVWT/ \ \ Please note the HTML Commentin jsFiddle, it seems there is a problem with git-edge on jsFiddle / code.jquery.com/jquery-git.js \ \ As already said, with you fix in jquery-git the Bug does not occur in Opera, but is still present in FF when testing with this fiddle. → 1301962046487691 |
---|
Yes, actually it seems if you enforce dataType:jsonp as ajax option, the bug appears no matter if cross-domain or not. So I now have set up a testcase on jsFiddle:
Please note the HTML Commentin jsFiddle, it seems there is a problem with git-edge on jsFiddle / code.jquery.com/jquery-git.js
As already said, with you fix in jquery-git the Bug does not occur in Opera, but is still present in FF when testing with this fiddle.
Changed April 05, 2011 04:25AM UTC by comment:12
keywords: | jsonp timeout → jsonp timeout needsdocs |
---|
OK, so I checked this under FF and it seems timmy is right: once the script tag has been appended, there seems to be no way to cancel loading. This is quite daunting seeing as it means there is no mean to abort cross-domain script requests in FF. I tried with and without async to no avail :(
This is clearly a bug in the browser. We need to document the issue.
Changed April 23, 2011 06:10PM UTC by comment:13
priority: | undecided → high |
---|
Changed June 29, 2011 03:30PM UTC by comment:14
milestone: | 1.6 |
---|---|
resolution: | fixed |
status: | closed → reopened |
Changed June 29, 2011 03:32PM UTC by comment:15
@jaubourg: as this ticket has been open for two months, can you re-confirm whether it's been fixed or if any fixes are currently being worked on for it? thanks!
Changed June 29, 2011 04:10PM UTC by comment:16
It's a FF bug. The script tag is removed, yet the script is still loaded and executed... didn't check if this was fixed in FF5.
Changed June 29, 2011 07:20PM UTC by comment:17
status: | reopened → open |
---|
Changed June 30, 2011 03:58PM UTC by comment:18
#9687 is a duplicate of this ticket.
Changed July 08, 2011 03:37PM UTC by comment:19
keywords: | jsonp timeout needsdocs → jsonp timeout neededdocs |
---|
Docs updated: http://api.jquery.com/jQuery.ajax/
Changed July 11, 2011 04:46PM UTC by comment:20
milestone: | → 1.next |
---|
Changed July 15, 2011 05:25PM UTC by comment:21
#9782 is a duplicate of this ticket.
Changed July 18, 2011 01:48PM UTC by comment:22
#9853 is a duplicate of this ticket.
Changed November 24, 2011 12:46AM UTC by comment:23
When i load this jsfiddle code: http://jsfiddle.net/XtVWT/
I always get an error in IE9 stating "Object expected". Clicking debug error points me to this line:
jQuery1604361509936638372_1322095521345({"_": "1322095521358", "echo": "Hello World!"});
This is a huge issue for us and hope it can get fixed soon.
Changed November 24, 2011 05:32AM UTC by comment:24
@blair: the jsFiddle you posted uses an old version of jquery, because at the time i reported the bug, jsFiddle has had issues with using jquery-edge, which is was likely fixed weeks ago.
I set up a new fiddle here: http://jsfiddle.net/HEsL2/ which you can use to test against jquery-edge. Can you confirm IE9 is affected, and if so, whether or not you are getting the "failed with error: timeout" message?
If you get the "right" error message that means the error() event is correctly triggered when hitting the timeout. If thats the case, the bug should not be a huge issue as the code will work as expected - the only drawback is the somewhat confusing error message that is displayed on the error console.
Changed November 24, 2011 06:00AM UTC by comment:25
@Dynalon
With your new example I still get the "Object Expected" error in IE9:
jQuery1725116137489204307943349415308069850127750664_1322114277554({"_": "1322114277629", "echo": "Hello World!"});
Also note that IE9 does not print its errors to the console, the error comes in the form of a modal alert dialog. This is a critical error that should be fixed asap as users with error messaging enabled in IE9 will always see this issue if the timeout hits, providing a less than ideal user experience.
Changed November 24, 2011 07:45AM UTC by comment:26
so you say IE9 will fail completely if the timeout hits? I agree, in this case this is a major issue. However, the whole thing isn't really jquery's fault, but specific to the browser implementations. To really fix the bug, one must nag the IE & FF developers to fix this in their code.
I don't think there is a *good* way to work around this in jquery for those browsers. The only workaround I came up with, is to prevent jquery from deleting the jsonp callback function when the timeout is encountered. Instead, upon timeout hit, the callback function is replaced by an empty dummy function which does nothing. So if - at a later time- the ajax request completes (even if the timeout was hit already) the browser actually finds the function.
This is UGLY AS HELL, as references to the callback functions are stored PERMANENTLY in the window object, thus cluttering the heap more and more everytime you do a jsonp request that exceeds its timeout.
If you can live with that and only do a limited amount of requests on a page, I've uploaded a patch that implements this workaround, grab it here: http://www.stud.uni-karlsruhe.de/~urblr/jquery-ugly/workaround-bug8744.diff
To test jquery with applied patch see: http://jsfiddle.net/D5YUZ/
At least it fixes the issues in FF for me (can't test in IE since I don't have a windows box around).
Once again, this is not a good solution and the patch is not meant to be put into upstream jquery, so you've been warned!
Changed November 24, 2011 07:54AM UTC by comment:27
Dynalon thanks for the reply, to clarify, the error callback is called into, but in addition, there is the "Object expected" error alert.
Changed November 24, 2011 07:58AM UTC by comment:28
Dynalon, your patch also fixes IE9 from throwing the "Object expected" error. It also correctly executes the ajax error callback.
I agree that cluttering the global namespace with callback functions is not a good solution, wondering if there is a better way.
Changed November 24, 2011 09:05AM UTC by comment:29
Yeah, this is a nasty nasty bug but Dynalon is spot on: the right fix is browser-side :(
Changed November 24, 2011 09:32AM UTC by comment:30
Turns out the bug is known since 2007 in FF but due to lack of reporter corresponding it never got fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=394908
Also, the Dojokit is aware of this bug, see: http://archive.dojotoolkit.org/nightly/dojotoolkit/dojo/tests/io/scriptTimeout.html
I've resurrected the bugreport in mozillas bugzilla and hopefully it can be worked out in FF.
Since I have no access to a windows machine to verify IE8/9 behaviour, could someone with windows report that bug to Microsoft and have them work it out?
Changed November 24, 2011 09:51AM UTC by comment:31
The following work-around was suggested a few months back in a different post:
window.jQuery16207753935731721285_1310374972748 = function() { delete window.jQuery16207753935731721285_1310374972748; }
Although the global namespace would still be littered with redundant callbacks at least the ones that DO eventually return would be removed.
Changed November 24, 2011 10:50AM UTC by comment:32
Replying to [comment:31 BennyTheSnitch]:
The following work-around was suggested a few months back in a different post:> window.jQuery16207753935731721285_1310374972748 = function() { > > delete window.jQuery16207753935731721285_1310374972748; > > } >Although the global namespace would still be littered with redundant callbacks at least the ones that DO eventually return would be removed.
On browsers that do handle removing a script tag properly or, in all browsers when you have a server error or the jsonp response is illformed and never calls the function, these would stay in the global namespace.
So it's still not an acceptable solution.
Changed December 13, 2011 01:30AM UTC by comment:33
Hi, what is the best workaround for this issue?
Changed March 23, 2012 11:02AM UTC by comment:34
My solution:
// JSON Request var auxTime = new Date(); var jQueryCallbackRandom = auxTime.getTime(); var callParameters = { url: myUrl, dataType: "jsonp", jsonp: "myServerParamCallback", jsonpCallback: "jQueryRandom_" + jQueryCallbackRandom, crossDomain : true, success: f_success, timeout: myCriticTimeOut, error: function(jqXHR, textStatus){ console.log("failed with error: " + textStatus); window["jQueryRandom_" + jQueryCallbackRandom] = function( {window["jQueryRandom_" + jQueryCallbackRandom] = null;}; }, data: params } $.ajax(callParameters);
Changed October 26, 2012 05:24PM UTC by comment:35
I've found this to still be an issue on at least IE9 and Windows Chrome. Replies #26, #32, and #34 successfully address it, but all will leave empty functions... Because I don't want to amend jQuery as in #26, I modified the answers in #32 and #34, see the following jsfiddle. Also, this doesn't require pre-defined jsonpCallback as in #34. Note this still leaves empty functions in the namespace...
Note that there's probably a there a better way to grab the pre-defined AJAX callback? I just found the callback as a property of the object, so I pulled the name with a for-in loop.
Changed October 26, 2012 05:26PM UTC by comment:36
Sorry I didn't realize using a # sign triggered links to tickets. I was referring to comment numbers.
Changed January 26, 2013 09:35PM UTC by comment:37
keywords: | jsonp timeout neededdocs → jsonp timeout |
---|---|
resolution: | → cantfix |
status: | open → closed |
It's pretty clear we can't make this work cross-browser, it will have to be solved by the browser makers. some workarounds are listed above. The related Firefox ticket is here:
https://bugzilla.mozilla.org/show_bug.cgi?id=707154
Docs ticket is here:
https://github.com/jquery/api.jquery.com/issues/235
Please don't add more comments to the ticket unless they contribute **significantly** to the discussion.
Changed April 01, 2013 03:51AM UTC by comment:38
#13693 is a duplicate of this ticket.
Unfortunately, and jaubourg can correct me on this, but I don't think it can be cancelled since jsonp is not xhr.