1

in jQuery, I iterate over an xml list of areas and do a POST request to get detailed information about each area. Because sending thousands of requests at once is debilitating for the client and server, I would like to set a flag so that I wait for a request to finish before sending the subsequent [next] request.

if the xml looks like this:

<area>5717</area>
<area>5287</area>
<area>5376</area>

then the xml parsing kinda looks like:

$(xml).find("area").each( function() {
  doPost();
}

and the doPost() function looks like

doPost : function () {
    $.post( ... )
}

Basically, I would like to add a toggling "wait" but I'm not sure how to achieve this. Is there a way I can keep the essential ".each" iteration or is another type of loop better for this?

Thanks in advance.

sova
  • 5,468
  • 10
  • 40
  • 48

4 Answers4

3

A general algorithm off the top of my head: You could put the whole list into an array. Take the first item of the array and post it. In the success handler of your post you could recursively call the function with the next index int the list.

I wouldn't use async: false because it would then be a blocking operation, which I assume the OP doesn't want.

Keith Rousseau
  • 4,435
  • 1
  • 22
  • 28
  • 1
    I assume async:false makes the POST synchronous, and that is not what I want, correct – sova Aug 09 '11 at 19:30
  • @Keith, minor nitpicky correction: the call would not be recursive. Callbacks are not recursive calls, the caller is not the previous stack frame. – Frédéric Hamidi Aug 09 '11 at 20:06
1

You can use:

$.ajaxSetup({async:false});

at the top of your script to make your AJAX calls synchronous.

Alternately, you can replace $.post() with $.ajax() and set the async flag to false.

George Cummins
  • 28,485
  • 8
  • 71
  • 90
  • Problem is that it then becomes a blocking operation – Keith Rousseau Aug 09 '11 at 19:17
  • ...as the OP requested. If the plan is to execute the posts sequentially, it seems safe to assume that other events need not be handled at the same time. @sova, can you confirm or deny this assumption? – George Cummins Aug 09 '11 at 19:19
  • Yeah, I guess I was operating under the assumption that this would be a background task on the page – Keith Rousseau Aug 09 '11 at 19:23
  • This is a new feature in our application to map certain bounded boxes on a google map, and it would be desirable to not block other operations, but while the user is "mapping" there is really not much else going on, other than a pretty visual, so this solution is also viable in my particular situation (but eventually, I would aspire to make it nonblocking) – sova Aug 09 '11 at 19:34
1

can you do a setTimeout ? that will allow for the function to still process asynchronous and allow for you to wait for some time in there too.

http://www.w3schools.com/js/js_timing.asp

setTimeout(function() {}, 5000)
Matt
  • 1,265
  • 1
  • 16
  • 24
  • setTimeout is useful in certain situations, but less reliable; there can be ~10,000 post requests for this (gets detailed info and draws it onto a map) so if they each wait 5 seconds that's a big bummer, and if it's not enough wait-time for the machine then the client will get way bogged down.. I do, however, appreciate the direction of thinking =) – sova Aug 09 '11 at 19:32
1

You can refactor your doPost() function to take the <area> element to process as an argument, and chain into the next element from your success callback. Something like:

(function doPost($area) {
    if ($area.length > 0) {
        $.post({
            // your options,
            success: function() {
                // your success handling...
                doPost($area.next("area"));
            }
        });
    }
})($(xml).find("area").first());

EDIT: Maybe the code above was a little too compact indeed.

Basically, the aim is to refactor your function so that it takes a jQuery object containing the next <area> element to process, or nothing if processing should stop:

function doPost($area) {
    if ($area.length > 0) {
        // Perform POST request and call ourselves from success callback
        // with next <area> element (or nothing if there's no such element).
    }
}

Then call this function with the first <area> element to process:

doPost($(xml).find("area").first());

The first code fragment in my answer does both at the same time. Functions are first-class objects in Javascript, and you can call a function you've just defined by enclosing its definition with parenthesis and providing the usual argument list, also surrounded by parenthesis.

Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • could you elaborate on the syntax a tad? the ending line ...})($(xml)... is somewhat boggling – sova Aug 09 '11 at 20:33
  • 1
    @sova, I probably compacted the code too much, see my updated answer. I personally do like that syntax, because it allows one to define a recursive or callback chain and initiate it in the same statement. Your mileage may vary, of course :) – Frédéric Hamidi Aug 09 '11 at 21:34
  • thank you for the concise explanation. I did not know you could group (function)(args) like that in js. Do you have a favorite javascript book or resource you would recommend? – sova Aug 10 '11 at 15:47
  • I'd recommend [MDN](https://developer.mozilla.org/en/JavaScript), which I find very complete and accurate. – Frédéric Hamidi Aug 10 '11 at 15:55