How do I catch jQuery $.getJSON (or $.ajax with datatype set to 'jsonp') error when using JSONP?

Solution 1:

Here's my extensive answer to a similar question.

Here's the code:

jQuery.getJSON(handlerURL + "&callback=?", 
    function(jsonResult){
        alert("Success!");
    })
.done(function() { alert('getJSON request succeeded!'); })
.fail(function(jqXHR, textStatus, errorThrown) { alert('getJSON request failed! ' + textStatus); })
.always(function() { alert('getJSON request ended!'); });

Solution 2:

It seems that JSONP requests that don't return a successful result never trigger any event, success or failure, and for better or worse that's apparently by design.

After searching their bug tracker, there's a patch which may be a possible solution using a timeout callback. See bug report #3442. If you can't capture the error, you can at least timeout after waiting a reasonable amount of time for success.

Solution 3:

Detecting JSONP problems

If you don't want to download a dependency, you can detect the error state yourself. It's easy.

You will only be able to detect JSONP errors by using some sort of timeout. If there's no valid response in a certain time, then assume an error. The error could be basically anything, though.

Here's a simple way to go about checking for errors. Just use a success flag:

var success = false;

$.getJSON(url, function(json) {
    success = true;
    // ... whatever else your callback needs to do ...
});

// Set a 5-second (or however long you want) timeout to check for errors
setTimeout(function() {
    if (!success)
    {
        // Handle error accordingly
        alert("Houston, we have a problem.");
    }
}, 5000);

As thedawnrider mentioned in comments, you could also use clearTimeout instead:

var errorTimeout = setTimeout(function() {
    if (!success)
    {
        // Handle error accordingly
        alert("Houston, we have a problem.");
    }
}, 5000);

$.getJSON(url, function(json) {
    clearTimeout(errorTimeout);
    // ... whatever else your callback needs to do ...
});

Why? Read on...


Here's how JSONP works in a nutshell:

JSONP doesn't use XMLHttpRequest like regular AJAX requests. Instead, it injects a <script> tag into the page, where the "src" attribute is the URL of the request. The content of the response is wrapped in a Javascript function which is then executed when downloaded.

For example.

JSONP request: https://api.site.com/endpoint?this=that&callback=myFunc

Javascript will inject this script tag into the DOM:

<script src="https://api.site.com/endpoint?this=that&callback=myFunc"></script>

What happens when a <script> tag is added to the DOM? Obviously, it gets executed.

So suppose the response to this query yielded a JSON result like:

{"answer":42}

To the browser, that's the same thing as a script's source, so it gets executed. But what happens when you execute this:

<script>{"answer":42}</script>

Well, nothing. It's just an object. It doesn't get stored, saved, and nothing happens.

This is why JSONP requests wrap their results in a function. The server, which must support JSONP serialization, sees the callback parameter you specified, and returns this instead:

myFunc({"answer":42})

Then this gets executed instead:

<script>myFunc({"answer":42})</script>

... which is much more useful. Somewhere in your code is, in this case, a global function called myFunc:

myFunc(data)
{
    alert("The answer to life, the universe, and everything is: " + data.answer);
}

That's it. That's the "magic" of JSONP. Then to build in a timeout check is very simple, like shown above. Make the request and immediately after, start a timeout. After X seconds, if your flag still hasn't been set, then the request timed out.