jQuery ajax (jsonp) ignores a timeout and doesn't fire the error event

JqueryAjaxError HandlingTimeoutJsonp

Jquery Problem Overview


To add some basic error handling, I wanted to rewrite a piece of code that used jQuery's $.getJSON to pull in some photo's from Flickr. The reason for doing this is that $.getJSON doesn't provide error handling or work with timeouts.

Since $.getJSON is just a wrapper around $.ajax I decided to rewrite the thing and surprise surprise, it works flawlessly.

Now the fun starts though. When I deliberately cause a 404 (by changing the URL) or cause the network to timeout (by not being hooked up to the interwebs), the error event doesn't fire, at all. I'm at a loss as to what I'm doing wrong. Help is much appreciated.

Here's the code:

$(document).ready(function(){

    // var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne"; // correct URL
    var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne_______"; // this should throw a 404

    $.ajax({
        url: jsonFeed,
        data: { "lang" : "en-us",
                "format" : "json",
                "tags" : "sunset"
        },
        dataType: "jsonp",
        jsonp: "jsoncallback",
        timeout: 5000,
        success: function(data, status){
            $.each(data.items, function(i,item){
                $("<img>").attr("src", (item.media.m).replace("_m.","_s."))
                          .attr("alt", item.title)
                          .appendTo("ul#flickr")
                          .wrap("<li><a href=\"" + item.link + "\"></a></li>");
                if (i == 9) return false;
            });
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERREUR: " + textStatus);
            alert("ERREUR: " + errorThrown);
        }
    });

});

I'd like to add that this question was asked when jQuery was at version 1.4.2

Jquery Solutions


Solution 1 - Jquery

jQuery 1.5 and higher have better support for error handling with JSONP requests. However, you need to use the $.ajax method instead of $.getJSON. For me, this works:

var req = $.ajax({
    url : url,
    dataType : "jsonp",
    timeout : 10000
});

req.success(function() {
    console.log('Yes! Success!');
});

req.error(function() {
    console.log('Oh noes!');
});

The timeout seems to do the trick and call the error handler, when there is no successful request after 10 seconds.

I did a little blogpost on this subject as well.

Solution 2 - Jquery

This is a known limitation with the native jsonp implementation in jQuery. The text below is from IBM DeveloperWorks

> JSONP is a very powerful technique for > building mashups, but, unfortunately, > it is not a cure-all for all of your > cross-domain communication needs. It > has some drawbacks that must be taken > into serious consideration before > committing development resources. > First and foremost, there is no error > handling for JSONP calls. If the > dynamic script insertion works, you > get called; if not, nothing happens. > It just fails silently. For example, > you are not able to catch a 404 error > from the server. Nor can you cancel or > restart the request. You can, however, > timeout after waiting a reasonable > amount of time. (Future jQuery > versions may have an abort feature for > JSONP requests.)

However there's a jsonp plug-in available on GoogleCode that provides support for error handling. To get started, just make the following changes to your code.

You can either download it, or just add a script reference to the plug-in.

<script type="text/javascript" 
     src="http://jquery-jsonp.googlecode.com/files/jquery.jsonp-1.0.4.min.js">
</script>

Then modify your ajax call as shown below:

$(function(){
    //var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne"; // correct URL
    var jsonFeed = "http://api.flickr.com/services/feeds/photos_public.gne_______"; // this should throw a 404	
    $.jsonp({
        url: jsonFeed,
        data: { "lang" : "en-us",
                "format" : "json",
                "tags" : "sunset"
        },
        dataType: "jsonp",
		callbackParameter: "jsoncallback",
        timeout: 5000,
        success: function(data, status){
            $.each(data.items, function(i,item){
                $("<img>").attr("src", (item.media.m).replace("_m.","_s."))
                          .attr("alt", item.title)
                          .appendTo("ul#flickr")
                          .wrap("<li><a href=\"" + item.link + "\"></a></li>");
                if (i == 9) return false;
            });
        },
        error: function(XHR, textStatus, errorThrown){
            alert("ERREUR: " + textStatus);
            alert("ERREUR: " + errorThrown);
        }
    });
});

Solution 3 - Jquery

A solution if you're stuck with jQuery 1.4:

var timeout = 10000;
var id = setTimeout( errorCallback, timeout );
$.ajax({
    dataType: 'jsonp',
    success: function() {
        clearTimeout(id);
        ...
    }
});

Solution 4 - Jquery

This may be a "known" limitation of jQuery; however, it does not seem to be well documented. I spent about 4 hours today trying to understand why my timeout was not working.

I switched to jquery.jsonp and it worked liked a charm. Thank you.

Solution 5 - Jquery

Seems to be resolved as of jQuery 1.5. I've tried the code above and I get the callback.

I say "seems to be" because the documentation of the error callback for jQuery.ajax() still has the following note:

> Note: This handler is not called for cross-domain script and JSONP requests.

Solution 6 - Jquery

The jquery-jsonp jQuery plugin mentioned in Jose Basilio's answer can now be found on GitHub.

Unfortunately the documentation is somewhat spartan, so I've provided a simple example:

    $.jsonp({
        cache: false,
        url: "url",
        callbackParameter: "callback",
        data: { "key" : "value" },
        success: function (json, textStatus, xOptions) {
            // handle success - textStatus is "success"   
        },
        error: function (xOptions, textStatus) {
            // handle failure - textStatus is either "error" or "timeout"
        }
    });

Important Include the callbackParameter in the call $.jsonp() otherwise I've found that the callback function never gets injected into the query string of the service call (current as of version 2.4.0 of jquery-jsonp).

I know this question is old, but the issue of error handling using JSONP is still not 'resolved'. Hopefully this update will assist others as this question is the top-ranked within Google for "jquery jsonp error handling".

Solution 7 - Jquery

What about listening to the script's onerror event ? I had this problem and solved it by patching jquery's method where the script tag was created. Here is the interesting bit of code :

// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {

	if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {

		cleanup();

		// Callback if not abort
		if ( !isAbort ) {
			callback( 200, "success" );
		}
	}
};

/* ajax patch : */
script.onerror = function(){
	cleanup();
	
	// Sends an inappropriate error, still better than nothing
	callback(404, "not found");
};

function cleanup(){
	// Handle memory leak in IE
	script.onload = script.onreadystatechange = null;

	// Remove the script
	if ( head && script.parentNode ) {
		head.removeChild( script );
	}

	// Dereference the script
	script = undefined;
}

What do you think of this solution ? Is it likely to cause compatibility issues ?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMatijsView Question on Stackoverflow
Solution 1 - JqueryHuskyView Answer on Stackoverflow
Solution 2 - JqueryJose BasilioView Answer on Stackoverflow
Solution 3 - JqueryBeto DealmeidaView Answer on Stackoverflow
Solution 4 - JqueryMarcView Answer on Stackoverflow
Solution 5 - JqueryPeter TateView Answer on Stackoverflow
Solution 6 - JqueryOiDatsMyLegView Answer on Stackoverflow
Solution 7 - JqueryHadrien MilanoView Answer on Stackoverflow