0

I have to make a bunch of JSONP calls, and execute some code after they all finish (or fail).

i.e.:

function finished() {
    alert('done!');
}

function method1() {
    return $.ajax('http://example.org/endpoint', {
        data: {
            foo: 'bar'
        },
        dataType: 'jsonp'
    });
}

function method2() {
    return $.ajax('http://example.org/endpoint', {
        data: {
            baz: 'qux'
        },
        dataType: 'jsonp'
    });
}

$.when(method1(), method2()).always(finished);

The only problem is, if any of the requests fail, finished() will not be called.

I can try and detect if one of the ajax calls fails like so:

function method1() {
    var method1_timeout = setTimeout(function() {
        // this call has failed - do something!
    }, 5000);

    var ajax = $.ajax('http://example.org/endpoint', {
        data: {
            foo: 'bar'
        },
        dataType: 'jsonp',
        success: function() {
            clearTimeout(method1_timeout);
        }
    });

    return ajax;
}

But I'm stuck at that point - how do I tell the deferred object that that particular ajax request has failed?

I tried calling the ajax object's error() method:

var method1_timeout = setTimeout(function() {
    ajax.error();
}, 5000);

But no luck.

Any ideas?

user37315
  • 13
  • 3
  • Pass a second function to `.then()`, which will be called if there are errors - http://api.jquery.com/jQuery.when/ (at the bottom). Or, probably preferred, use a promise's `fail` method - http://api.jquery.com/deferred.fail/ – Ian Jul 16 '14 at 06:52
  • The ajax `error()` method is not called for a failed JSONP request, so this won't work. I've updated my question to use `always()`. – user37315 Jul 16 '14 at 06:55
  • I see I see, sorry, I didn't realize that was the case (I've never had to use JSONP) – Ian Jul 16 '14 at 06:56
  • And sorry, with your code, you wouldn't want to use `ajax.error()`...you'd use `ajax.reject()` (for what you seem to want to do) - http://api.jquery.com/deferred.reject/ . This actually changes the state of the promise, which is what the callback methods for promises rely on. – Ian Jul 16 '14 at 06:57
  • `ajax` is the actual ajax object, not the `deferred` object... calling `error()` was just a guess, I hoped it would fail it. I'm not so sure calling `reject()` on the deferred object is a good idea either, as this will change the status to reject immediately, and fire my `finished()` method straight away, even if there are other ajax requests pending. – user37315 Jul 16 '14 at 07:07
  • `ajax` **is** a deferred object (that's what `$.ajax()` returns). And unless I'm mistaken, `finished` will not be called until all promises passed to `$.when` have completed (either succeeded or failed, doesn't matter). Rejecting one (which is the correct thing to do here), does not make the `always()` execute, because you passed 2 promises. When both promises are not pending, that's when the `always()` will be executed – Ian Jul 16 '14 at 07:18
  • Yes you're right, but `reject()` doesn't seem to be available to in the ajax object. – user37315 Jul 16 '14 at 07:20
  • Hmmm it seems setting a timeout in the ajax request itself will trigger the `error()` method, and cause the `deferred` object to fail. I'll add this as an answer when the time limit it up. – user37315 Jul 16 '14 at 07:24

2 Answers2

0

I tested your posted code 'as-is' in jsFiddle and it works, even when the JSONP calls fails (of course, requests returns 404 errors because of example.com/endpoint queries).

So I think I found the answer of your problem in the jQuery documentation, in the $.ajax chapter:

The server should return valid JavaScript that passes the JSON response into the callback function. $.ajax() will execute the returned JavaScript, calling the JSONP callback function, before passing the JSON object contained in the response to the $.ajax() success handler.

Check if your JSONP returned JS code are valid and check if you don't have any syntax error in your console that will stop the execution of your JS code on your side (and prevent the always function to be executed).

EDIT: I reproduced your error. When using jQuery 2.x (edge), it works like intended (try there). Passing to jQuery 1.x (edge), the two methods calls are working but the always func is never called (try there). All my tests have been done under Google Chrome.

SiZiOUS
  • 638
  • 1
  • 8
  • 9
-1

setTimeouts are in the global scope in JS and you created the variable ajax inside your function. Try

var method1_timeout = function(){ ajax.error();}

inside the function in which you defined var ajax and then provide method1_timeout to your setTimeout as a callback.

setTimeout(method1_timeout, 5000);
danyamachine
  • 1,848
  • 1
  • 18
  • 21