views:

49

answers:

3

I've got a particular function I want to run once, and only after the completion of several AJAX requests.

My current solution looks a bit like this:

function doWork() {
    //This is the function to be run once after all the requests
}

//some tracking/counting variables
var ajaxDoneCounter = 0;
var numOfAjaxRequests = 5;
var workDone = false;

function doWorkTrigger() {
    ajaxDoneCounter++;
    if( !workDone && ajaxDoneCounter >= numOfAjaxRequests ) {
        workDone = true;
        doWork();
    }
}

// ...

//and a number of ajax requests (some hidden within functions, etc)
//they look something like this:
$.ajax({
    url: "http://www.example.com",
    dataType: "json",
    success: function( data ) {
        //load data in to variables, etc
        doWorkTrigger();
    }
});

One obvious pitfall in the above is that any AJAX call that is not successful will not increment ajaxDoneCount and so doWork() will probably never be called. I can get around that using the error callback in inside any $.ajax, so that doesn't worry me too much.

What I want to know is whether the above is safe and/or good practice?
Is there a trick I've missed, or any thing else that might work better?

+2  A: 

I would use .ajaxComplete(), it will be triggered whenever an Ajax call completed (success or error):

var numOfAjaxRequests = 5;

$(document).ajaxComplete(function() {
    numOfAjaxRequests--;
    if(!numOfAjaxRequests) {
        doWork();

    }
});

Then you don't have to edit every Ajax request.

You could even use .ajaxSend() to get notified of starting Ajax requests, instead of hardcoding it (but I am not sure whether this really works, maybe you will experience race conditions):

var numOfAjaxRequests = 0;

$(document).ajaxSend(function() {
    numOfAjaxRequests++;
});
Felix Kling
Ah, good to know - but what if I've got two sets of Ajax calls each triggering a sepereate functions (I don't, I'm just wondering). Would this method allow me to seperate the two sets so both functions don't stick waiting for all of both groups it finish?
DMA57361
@DMA57361: Have a look at the documentation. The XMLHttpRequest and the options of the Ajax call will be passed to the callback. You might be able to distinguish the calls with this information. In the documentation is an example that the callback is only executed if the Ajax call was to a specific URL.
Felix Kling
@Felix Looks good to me, think I shall have a good read of the doc's there and investigate these two a bit further. Thanks.
DMA57361
A: 

I don't know enough about JavaScript internals, but there is a danger that the operation:

ajaxDoneCounter++;

is not atomic. If that is the case, then this could be subject to a race condition.

Matthew Schinckel
Why the downvote? If ++ is atomic in JavaScript, then post a comment about it. My quick research after posting the answer seems to indicate that ECMAScript does not declare if ++ is atomic or not.
Matthew Schinckel
@Matthew I was wondering about this -1 as well, and I've found [this](http://stackoverflow.com/questions/1663125/is-javascript-multithreaded) question, which seems to indicate JavaScript is only executed as a single thread, so presumably it will just queue up the ajax callbacks as they finish - meaning this particular issue isn't relevant. Still, it would have been helpful if whoever did downvote had bothered to *tell* us that...
DMA57361
Yep, and the comment at http://stackoverflow.com/questions/1663125/is-javascript-multithreaded/1663578#1663578 indicates that it may not always be so.
Matthew Schinckel
+1  A: 

I think you should use complete(XMLHttpRequest, textStatus) ajax event instead of success(data, textStatus, XMLHttpRequest).

According to jQuery help:

complete(XMLHttpRequest, textStatus)

A function to be called when the request finishes (after success and error callbacks are executed). The function gets passed two arguments: The XMLHttpRequest object and a string describing the status of the request. This is an Ajax Event.

gyromonotron