I've found a workaround to get an error callback with an empty response in cross domain requests.
In this example I'm using Zepto, a light-weight version of jQuery, but I assume that this works fine in jQuery too.
First of all, you must use these parameters to do a cross domain request:
$.ajax({
    url: url,
    type: 'GET',
    dataType: 'jsonp',
    contentType: 'application/x-javascript',
    crossDomain: true,
    success: function (data, status) { /* ... */ }
    error: function () { /* ... */ }
    // ...
Now, the $.ajax function internally use the $.ajaxJSONP for crossdomain requests. This is the original Zepto $.ajaxJSONP function:
  $.ajaxJSONP = function(options){
    var callbackName = 'jsonp' + (++jsonpID),
      script = document.createElement('script'),
      abort = function(){
        $(script).remove()
        if (callbackName in window) window[callbackName] = empty
        ajaxComplete('abort', xhr, options)
      },
      xhr = { abort: abort }, abortTimeout
    if (options.error) script.onerror = function() {
      xhr.abort()
      options.error()
    }
    window[callbackName] = function(data){
      clearTimeout(abortTimeout)
      $(script).remove()
      delete window[callbackName]
      ajaxSuccess(data, xhr, options)
    }
    serializeData(options)
    script.src = options.url.replace(/=\?/, '=' + callbackName)
    $('head').append(script)
    if (options.timeout > 0) abortTimeout = setTimeout(function(){
        xhr.abort()
        ajaxComplete('timeout', xhr, options)
      }, options.timeout)
    return xhr
  }
My workaround is very simple and consists on an interval called few times on the script.onload event handler, in order to verify that the callback function was called.
This is my version of the $.ajaxJSONP function:
$.ajaxJSONP = function(options){
    var called = false, // Flag to check that callback was called
        callbackName = 'jsonp' + (++jsonpID),
        script = document.createElement('script'),
        abort = function(){
            $(script).remove()
            if (callbackName in window) window[callbackName] = empty
            ajaxComplete('abort', xhr, options)
        },
        xhr = { abort: abort }, abortTimeout
    if (options.error) {
        script.onerror = function() {
            xhr.abort()
            options.error()
        };
        // IMPORTANT!!!
        script.onload = function () {
            var times = 0;
            var interval = setInterval(function () {
                // After 5 intervals, if the callback wasn't called, returns an error
                if (times++ == 5) {
                    clearInterval(interval);
                    if (!called) {
                        options.error();
                    }
                } else if (called) {
                    clearInterval(interval);
                }
            }, 100);
        };
    }
    window[callbackName] = function(data){
        // Setting the "called" flag to true
        called = true;
        clearTimeout(abortTimeout)
        $(script).remove()
        delete window[callbackName]
        ajaxSuccess(data, xhr, options)
    }
    serializeData(options)
    script.src = options.url.replace(/=\?/, '=' + callbackName)
    $('head').append(script)
    if (options.timeout > 0) abortTimeout = setTimeout(function(){
        xhr.abort()
        ajaxComplete('timeout', xhr, options)
    }, options.timeout)
    return xhr
}
Note: If you are interested in the server side behavior, please see the beginning of this tutorial: http://phonegap.com/2011/07/20/making-jsonp-calls-with-zepto-on-android-device/