Q:

jQuery.ajax - always() not always running

I'm using jQuery.ajax to make a REST call and retrieve some JSON. It's working as expected. However, when I force an error condition, such as an invalid URL, the always method does not fire. If I set crossDomain=false or dataType='json', then always does fire. But, I can't do that in my production code. If you set url='http://ip.jsontest.com/' then always fires. I created a small example to illustrate the problem:
var jqXHR = jQuery.ajax({
    type: 'GET',
    async: false,
    cache: false,
    url: 'http://ip.jsontest.com.BADURL/',
    contentType: 'application/json; charset=UTF-8',
    crossDomain: true,
    dataType: 'jsonp'
})
.done(function (data, textStatus, jqXHR) {
    console.log('Your IP is ' + data.ip);
    console.log('done was called');
})
.fail(function (jqXHR, textStatus, errorThrown) {
    console.log('fail was called');
})
.always(function (dataOrjqXHR, textStatus, jqXHRorErrorThrown) { console.log('always was called'); });

You can run this in the console at jquery.com which is using jQuery 1.9.1. I have the same behavior using jQuery 1.11.1. I need always to fire to handle times when the url is unavailable. I get the same behavior in IE11, Chrome 38 and FF 33. Am I doing something wrong or is this a bug? 
Thanks.

A:

This is something that is known for JSONP calls.  According to the $.ajax reference for error:

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

Also note that synchronous JSONP calls are not supported:

Cross-domain requests and dataType: "jsonp" requests do not support synchronous operation.

Workarounds typically involve either 1) setting a timeout for the call or 2) using a plugin to add more typical error functionality.
1)  Setting a timeout (and async true)
var jqXHR = jQuery.ajax({
    type: 'GET',
    async: true,
    cache: false,
    url: 'http://ip.jsontest.com.BADURL/',
    contentType: 'application/json; charset=UTF-8',
    crossDomain: true,
    dataType: 'jsonp',
    timeout: 2000
})
.fail(function (jqXHR, textStatus, errorThrown) {
    console.log('fail was called');
})
.done(function (data, textStatus, jqXHR) {
    console.log('Your IP is ' + data.ip);
    console.log('done was called');
})
.always(function (dataOrjqXHR, textStatus, jqXHRorErrorThrown) { console.log('always was called'); });

2) The jQuery JSONP plugin which adds error recovery features.

