70

I am trying to use the jQuery.when to fire two ajax requests and then call some function after the two requests have completed. Here's my code:

var count = 0;
var dfr;

var showData = function(data) {
    dfr.resolve();
    alert(count);
   // Do something with my data data received
};

var method1 = function() {
    dfr = $.Deferred();

    return $.ajax('localhost/MyDataService/DataMethod_ReturnsData', {
        dataType: "jsonp",
        jsonp: "$callback",
        success: showData
    });
};

var method2 = function() {
    return $.ajax('localhost/MyDataService/DataMethod_ReturnsCount', {
        dataType: "jsonp",
        jsonp: "$callback",
        success: function(data) {
            count = data.d.__count;
        }
    });
};

$.when(method1(), method2())
    .then(showData());

However this is not working as expected. Ajax call in method1 will return data which is to be used in showData() and Ajax call in method2 will return count which is to be assigned to var count and later used in showData().

But when I fire the above code, method1 gets called and then method2 and then showData leaving the data in showData as 'undefined'. How can I achieve this via $.when which as far as I know proceeds only when both functions returning $.promise are executed. I want that both the ajax calls should be called in parallel and later results be displayed based on results from both calls.

taras
  • 6,566
  • 10
  • 39
  • 50
Ashish
  • 2,544
  • 6
  • 37
  • 53
  • You don't need the `dfr = $.Deferred()` at all, because the [`jqXhr`](http://api.jquery.com/Types/#jqXHR) returned by `$.ajax()` is a deferred. Not sure if that will fix the problem here, but it's definitely unnecessary. The problem might be that you're using `.then()` instead of `.done()`. – Matt Ball Mar 12 '11 at 05:14
  • I was referring to the post by eric hynds [http://www.erichynds.com/jquery/using-deferreds-in-jquery/] where he too used the $.when and then callbacks. – Ashish Mar 12 '11 at 05:22
  • 1
    you only need that if you're using an object which doesn't handle deferred's directly. Eric's demo uses a timer object, so he has to manaully "resolve" a deferred object that he created himself when the timer expires. Like Matt says, if it's an AJAX query you don't need it. – Alnitak Mar 12 '11 at 16:58
  • remove success from methods and use $.when(m1(),m2()).then(m3) with m3 = function(data1, data2) – Guillaume86 Mar 12 '11 at 17:10
  • @Guillaume86 : I tried to implement your suggestion for removing success with m3 = function(data1, data2) but both the values, data1 and data2, came out to be undefined. Can u modify my code here [http://jsfiddle.net/f4hmL/3/] to show how it would work..? – Ashish Mar 13 '11 at 05:29

3 Answers3

76
function showData(data1, data2) {
    alert(data1[0].max_id);
    alert(data2[0].max_id);
}

function method1() {
    return $.ajax("http://search.twitter.com/search.json", {
        data: {
            q: 'ashishnjain'
        },
        dataType: 'jsonp'
    });
}

function method2() {
    return $.ajax("http://search.twitter.com/search.json", {
        data: {
            q: 'ashishnjain'
        },
        dataType: 'jsonp'
    });
}

$.when(method1(), method2()).then(showData);​

Here's a working jsFiddle

Simon P Stevens
  • 27,303
  • 5
  • 81
  • 107
Guillaume86
  • 14,341
  • 4
  • 53
  • 53
  • That's gr8. Thanks. However what I saw after implementing it in my internal code is that, I get data1 and data2 same. That means, I can remove data2 and use data1 object. Data1 comes out to be [Object], "success", [Object]. The 1st [Object] is response from my first ajax call, "success" is string, and 2nd [Object] is response from my second ajax call. I can evaluate it as data1[0].d. However data1[2].responseText is the result from 2nd ajax call. Why do we get responseText rather than data[2].d? Any suggestions? – Ashish Mar 14 '11 at 03:44
  • looks weird, data1 should be an array containing the arguments of the m1() success callback, and data2 an array with the args of the m2() callback – Guillaume86 Mar 14 '11 at 09:10
  • _In the case where multiple Deferred objects are passed to jQuery.when, the method returns the Promise from a new "master" Deferred object that tracks the aggregate state of all the Deferreds it has been passed._ Quote from manual. – HarryFink Aug 09 '13 at 14:47
  • Doesn't explain how the master deferred is resolved arguments wise, wich is what was asked over 2 years ago ;) – Guillaume86 Aug 09 '13 at 14:54
  • The fiddle does not work. Blocked a frame with origin "http://fiddle.jshell.net" from accessing a frame with origin "http://jsfiddle.net". Protocols, domains, and ports must match. – oligofren Oct 21 '13 at 20:46
  • Chrome updates broke a lot of fiddles that were using raw.github urls. You can just find alternative urls for the scripts and it should be OK. – Guillaume86 Nov 22 '13 at 11:54
  • @Guillaume86 hello, it is possible to create dynamic method()? for example I can use 2 method or 3 base on the condition meet. – Mathew Magante Feb 12 '20 at 06:33
  • Use `Promise.all( arrayOfPromises )` – Guillaume86 Mar 09 '20 at 10:32
  • Great solution. I have a question: how do I pass parameters to method1 and method2 in the when call? – SoftwareDveloper Aug 09 '21 at 15:12
36

The problem is that you're passing showData() to then(), not showData. You should pass a reference to a function to .then():

$.when(method1(), method2())
    .then(showData);

or

$.when(method1(), method2())
    .then(function () {
        showData();
    });

Edit

I've put together a working demo. Part of the problem (at least in the code fragment you posted) was that there was no callback function named $callback. Part of the problem was the $ in the callback name '$callback'.

So, remove the jsonp: '$callback' ajax option, so that jQuery defaults to a callback function named callback, and define a function with that name, and it all works.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • Tried both ways but still the same. data parameter in showData is still 'undefined' – Ashish Mar 12 '11 at 06:42
  • See my edit. There were at least 2 other problems in your code. – Matt Ball Mar 12 '11 at 16:38
  • yup, looks right to me - if you call `.then(showData())` it will execute `showData` immediately and then pass its result to `.then`. As Matt says - pass the function reference. – Alnitak Mar 12 '11 at 16:53
  • Thanks a lot Matt. With the help of your sample I put across a working sample here [http://jsfiddle.net/f4hmL/3/]. – Ashish Mar 13 '11 at 05:26
-4

I have little bit modified your code and made simpler to understand... i haven't test it please try it

var count = 0;
function countResponse(data) {
    count++;
    if(count==2)
    {
        // Do something after getting responce from both ajax request
    }
};

var method1 = function() {
    return $.ajax('localhost/MyDataService/DataMethod_ReturnsData', {
        dataType: "jsonp",
        jsonp: "$callback",
        success: function(data) {
            countResponse(data)
        }
    });
};

var method2 = function() {
    return $.ajax('localhost/MyDataService/DataMethod_ReturnsCount', {
        dataType: "jsonp",
        jsonp: "$callback",
        success: function(data) {
            countResponse(data)
        }
    });
};
Haresh Vidja
  • 8,340
  • 3
  • 25
  • 42
  • 1
    This does not answers my query as the countReponse will loose any one of the data, either count or actual data, since the ajax calls in method1 and method2 are asynchronous. Whatever call executes later will have data but the first data will be lost. – Ashish Mar 12 '11 at 06:22
  • 2
    the point of jQuery deferreds is so that you don't have to count the number of responses before proceeding. – Alnitak Mar 12 '11 at 16:50
  • 2
    Agreed with previous two comments. This does not answer the question at all - esp. since it doesn't use jQuery deferreds. – Matt Ball Mar 12 '11 at 16:55
  • Sorry Ashish for my misunderstanding because you have not mentioned both of response is need to store. – Haresh Vidja Mar 14 '11 at 04:23