0

I'm using $.when().then(success, fail).always() model to send 3 asynchronous ajax calls.

here's my code

$.when(
    $.ajax({
        url: NewsCategoryUrl,
        beforeSend: function () {
            //alert('NewsCategoryUrl berfore send by Boda');
        },
        headers: {
            'accept': 'application/json;odata=verbose',
            'content-type': 'application/json;odata=verbose'
        },
        success: function (data) {
            //alert('NewsCategoryUrl done');
            $.each(data.d.results, function (index, item) {
                NewsCategoryList.push(G_G.GetNewsCategoryObject(item));
                //alert(JSON.stringify(NewsCategoryList[index]));
            });
            // alert('after loop: ' + NewsCategoryList);
        },
        error: function (jqXHR, errorThrown) {
            alert(errorThrown.stack);
        }
    }),
    $.ajax({
        url: NewsFeedUrl,
        beforeSend: function () {
            alert('NewsFeedUrl berfore send by Boda');
        },
        headers: {
            'accept': 'application/json;odata=verbose',
            'content-type': 'application/json;odata=verbose'
        },
        success: function (data) {
            //alert('NewsFeedUrl done');
            $.each(data.d.results, function (index, item) {
                NewsFeedList.push(G_G.GetNewsFeedObject(item));
                //alert(JSON.stringify(NewsFeedList[index]));
            });
            //alert('after loop: ' + NewsFeedList);

        },
        error: function (jqXHR, errorThrown) {
            alert(errorThrown.stack);
        }
    }),
    $.ajax({
        url: UpdatesUrl,
        beforeSend: function () {
            alert('UpdatesUrl berfore send by Boda');
        },
        headers: {
            'accept': 'application/json;odata=verbose',
            'content-type': 'application/json;odata=verbose'
        },
        success: function (data) {
            //alert('UpdatesUrl done');
            $.each(data.d.results, function (index, item) {
                updates.push(G_G.GetUpdateObject(item));
                //alert(JSON.stringify(updates[index]));
            });
            //alert('after loop: ' + updates);
        },
        error: function (jqXHR, errorThrown) {
            alert(errorThrown.stack);
        }
    })).then(function () {
    /*                alert("got 'ya all :D");
                    alert(NewsCategoryList);
                    alert(NewsFeedList);
                    alert(updates);*/
    SaveUpdatesToLocalstorage(updates);
    NewsCategoryReandering();
    SliderRendering(3);
}, function () {
    alert('some went wrong');
}).always(function () {
    alert('in always');
    myApp.closeModal('.popup-splash');
});

the problem is that when I try to comment out any of the beforeSend in the 2nd or 3rd ajax calls, I end up in the fail section with alert ("some went wrong") and undefined results..

All I could find after googling it, that It's about timing issue (mostly about synchronous requests unlike mine), and one SO answer about ASI which I couldn't verify over my case..

note that when I commented the first alert in the first ajax request everything continue to work normally..

Any help?

Boda
  • 329
  • 2
  • 11
  • 1
    Well, the arguments to that function tell you *what* went wrong -- what was it? – T.J. Crowder Feb 29 '16 at 18:14
  • Pattern won't work using `success`. Use `then()` of each individual request to return the updated arrays. Your `when(0` needs those arrays to be compiled also and timing will be off your way – charlietfl Feb 29 '16 at 18:56
  • @T.J.Crowder I didn't know that fail call back accepts args "thanks for that, helped a bit", but when I saw the message it didn't really help, error msg said that the request is not authorized!, although the same code "with the same authorized model" works without it.. – Boda Mar 01 '16 at 13:11
  • @charlietfl I'm sorry, I don't get what you mean.. – Boda Mar 01 '16 at 13:12
  • What I mean is you can't return anything to a `success` callback but you can return to a `then()` and that `then()` will execute before the `$.when()` one does. That's how promises work ... always return in `then()` what gets passed to next promise in the chain. In your case you can't determine the order of the processing of `$.each` occurring in `success` before or after the `$.when` completes – charlietfl Mar 01 '16 at 13:24

3 Answers3

0

From what I see in the code, the problem as you mention could be that you are trying to call 3 ajax async calls in the "when" predicate, and maybe (just maybe) you need them to be executed in an specific order, I mean, maybe first you need to end the call to NewsCategoryUrl then continue to "NewsFeedUrl and then to UpdatesUrl.

When you have the alert, just the time spent in the dialog to be shown and confirm may be enough for the 3 calls to be completed in the right order and all is ok in this case.

Hope this helps

JulioCT
  • 1,057
  • 8
  • 7
  • if that is right, what's the difference between that and calling without when? – Boda Mar 01 '16 at 13:15
  • hello. yes is right, and i see the timing solution you have added, which is basically the same. just calling the `alert` and closing it was giving you the delay needed for it to work ;) – JulioCT Mar 01 '16 at 13:27
  • the reason can be in your server side logic, clearly you need to execute `NewsCategoryUrl` before the others. why don't you, instead of adding the `sleepFor` just make the second and third ajax calls in the `sucess` callback function for the first call? – JulioCT Mar 01 '16 at 13:30
  • yes it was, still don't know why thought = ) .. if requests has to wait each other, what's the point of the whole callbacks/asynchronous then? – Boda Mar 01 '16 at 13:31
  • nope, I don't think so about NewsCategoryUrl before the others, because I switched the requests and I got the same results, only the first request (no matter which) is being done correctly :D .. – Boda Mar 01 '16 at 13:34
  • @Boda if the data of subsequent requests are dependent on previous requests it only makes sense to need the previous to complete first so the data is available for the next – charlietfl Mar 01 '16 at 13:34
  • @charlietfl but that is not the case here!, all the arrays is not dependent at all, each has its own data as you can see.. as I understand $.when() it should wait until each one of 3 requests are compleated then calls the $.then() .. so there aren't any dependencies here.. right? – Boda Mar 01 '16 at 13:40
  • @Boda OK.. then it's your processing of the data order that is the problem because you are assuming the `success` will run before `then` of `$.when` – charlietfl Mar 01 '16 at 13:44
  • @charlietfl: **because you are assuming the success will run before then of $.when** .. isn't that the case? – Boda Mar 01 '16 at 14:41
  • @Boda Not necessarily ... because it isn't part of the promise chain whereas using `then()` is. I believe if you used `console.log()` instead of `alert` in all of those you would see order isn't correct. See my answer – charlietfl Mar 01 '16 at 14:44
  • @charlietfl I saw it and I'll try it ASAP, the thing is I can't use console.log because it's a Cordova/PhoneGap mobile app, so I don't get to see the console. – Boda Mar 01 '16 at 14:52
  • @Boda OK..then create an element you can append messages to for this test – charlietfl Mar 01 '16 at 14:54
0

turned out to be a timing issue(still don't know how/why is that!)..

for future reference, final code as follow:

function sleepFor(sleepDuration) {
    var now = new Date().getTime();
    while (new Date().getTime() < now + sleepDuration) { /* do nothing */ }
};

$.when(
    $.ajax({
        url: NewsCategoryUrl,
        beforeSend: function () {
            //alert('NewsCategoryUrl berfore send by Boda');
        },
        headers: {
            'accept': 'application/json;odata=verbose',
            'content-type': 'application/json;odata=verbose'
        },
        success: function (data) {
            //alert('NewsCategoryUrl done');
            $.each(data.d.results, function (index, item) {
                NewsCategoryList.push(G_G.GetNewsCategoryObject(item));
                //alert(JSON.stringify(NewsCategoryList[index]));
            });
            // alert('after loop: ' + NewsCategoryList);
        },
        error: function (jqXHR, errorThrown) {
            alert(errorThrown.stack);
        }
    }),
    $.ajax({
        url: NewsFeedUrl,
        beforeSend: function () {
            //alert('NewsFeedUrl berfore send by Boda');
             sleepFor(350);
        },
        headers: {
            'accept': 'application/json;odata=verbose',
            'content-type': 'application/json;odata=verbose'
        },
        success: function (data) {
            //alert('NewsFeedUrl done');
            $.each(data.d.results, function (index, item) {
                NewsFeedList.push(G_G.GetNewsFeedObject(item));
                //alert(JSON.stringify(NewsFeedList[index]));
            });
            //alert('after loop: ' + NewsFeedList);

        },
        error: function (jqXHR, errorThrown) {
            alert(errorThrown.stack);
        }
    }),
    $.ajax({
        url: UpdatesUrl,
        beforeSend: function () {
            //alert('UpdatesUrl berfore send by Boda');
            sleepFor(350);
        },
        headers: {
            'accept': 'application/json;odata=verbose',
            'content-type': 'application/json;odata=verbose'
        },
        success: function (data) {
            //alert('UpdatesUrl done');
            $.each(data.d.results, function (index, item) {
                updates.push(G_G.GetUpdateObject(item));
                //alert(JSON.stringify(updates[index]));
            });
            //alert('after loop: ' + updates);
        },
        error: function (jqXHR, errorThrown) {
            alert(errorThrown.stack);
        }
    })).then(function () {
    /*                alert("got 'ya all :D");
                    alert(NewsCategoryList);
                    alert(NewsFeedList);
                    alert(updates);*/
    SaveUpdatesToLocalstorage(updates);
    NewsCategoryReandering();
    SliderRendering(3);
}, function () {
    alert('some went wrong');
}).always(function () {
    alert('in always');
    myApp.closeModal('.popup-splash');
});

PS: sleepFor function can be found here.

Community
  • 1
  • 1
Boda
  • 329
  • 2
  • 11
0

The following uses then() of each individual request to return the arrays to $.when promise.

The difference is you are controlling the order of the data processing such that $.when can't complete until each of the requests have returned to it.

Each request itself may complete in a different order than you call them, but the console.log in the $.when callback will always be after the processing of requests themselves and the 3 arrays are also now shown as arguments of the when callback

The way you are using success does not assure correct data processing order the same way this approach does

var firstReq=$.ajax({
    url: NewsCategoryUrl,    
    headers: {
        'accept': 'application/json;odata=verbose',
        'content-type': 'application/json;odata=verbose'
    }
}).then(function(data){
    $.each(data.d.results, function (index, item) {
        NewsCategoryList.push(G_G.GetNewsCategoryObject(item));
    }); 
    console.log('Process NewsCategoryList')
    // will be returned to `$.when.done()`
    return NewsCategoryList;
});

var secondReq= $.ajax({
    url: NewsFeedUrl,    
    headers: {
        'accept': 'application/json;odata=verbose',
        'content-type': 'application/json;odata=verbose'
    }
}).then(function (data) {    
    $.each(data.d.results, function (index, item) {
        NewsFeedList.push(G_G.GetNewsFeedObject(item));
    });    
    console.log('Process NewsFeedList');
    // return to $.when
    return NewsFeedList;    
});

var thirdReq = $.ajax({
    url: UpdatesUrl,    
    headers: {
        'accept': 'application/json;odata=verbose',
        'content-type': 'application/json;odata=verbose'
    }
}).then(function (data) {
    $.each(data.d.results, function (index, item) {
        updates.push(G_G.GetUpdateObject(item));
    });
    console.log('Process updates')
    return updates;
});


$.when(firstReq ,secondReq,thirdReq)
    // data in arguments is a result of each `then()` above
    .done(function (NewsCategoryList,NewsFeedList,updates ) {
    console.log('Start storage save and rendering')
    SaveUpdatesToLocalstorage(updates);
    NewsCategoryReandering();
    SliderRendering(3);
}).fail( function () {
    alert('some went wrong');
}).always(function () {
    alert('in always');
    myApp.closeModal('.popup-splash');
});
charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • tried it, same as before, only the frist one get throw, the rest of them fails with the same error message .. – Boda Mar 01 '16 at 15:34
  • Would be fairly simple to test all this in a simple web page and use console to debug – charlietfl Mar 01 '16 at 15:43