1

I'm trying to send multiple post within a do while loop but the result is not added

<script type="text/javascript">
function action() {
    var initval = 1;
    var endval = 5;
    do {
    var action_string = 'txtuser=someone';

    $.ajax({
                    type: "POST",
                    url: "http://localhost/js.php",
                    data: action_string,
                    success: function(result){
                       $('div#append_result').append(initval + ',<br/>');
                     }  
                });
    initval++;
     } while (initval <= endval);
  }
</script>

The Output is: 5, 5, 5, 5, 5,

and I need the output to be: 1, 2, 3, 4, 5,

Saro Taşciyan
  • 5,210
  • 5
  • 31
  • 50
blackriderws
  • 833
  • 2
  • 9
  • 14

3 Answers3

6

Due to the async nature of AJAX, by the time your success function runs for any of the resulting AJAX requests, the loop has completed and initval is set to 5. You need to capture the state of initval at the start of each request and use that captured state in the success() method. Closing over the value is the simplest way to do it:

function action() {
    var initval = 1;
    var endval = 5;
    do {
        var action_string = 'txtuser=someone';

        ( function( captured_initval ){
            $.ajax({
                type: "POST",
                url: "http://localhost/js.php",
                data: action_string,
                success: function(result){
                    $('div#append_result').append(captured_initval + ',<br/>');
                }  
            });
        }( initval ) );

        initval++;
    } while (initval <= endval);
}

Understand, though, that one or more requests could get hung up at the server allowing a latter request to complete first, which could result in 1, 2, 5, 3, 4 or something like that.

Also, using an element's ID is much faster than prefixing the hash selector with the elements tag name. Plus you should avoid re-querying the DOM for your result DIV every time the success runs. Grab it once and use it when needed:

function action() {
    var initval = 1;
    var endval = 5;
    do {
        var action_string = 'txtuser=someone',
            $AppendResult = $('#append_result');

        ( function( captured_initval ){
            $.ajax({
                type: "POST",
                url: "http://localhost/js.php",
                data: action_string,
                success: function(result){
                    $AppendResult.append(captured_initval + ',<br/>');
                }  
            });
        }( initval ) );

        initval++;
    } while (initval <= endval);
}
JAAulde
  • 19,250
  • 5
  • 52
  • 63
  • 1
    You aren't "closing over" the value of *interval*, you are avoiding the effects of the closure (which still exists). – RobG Aug 05 '11 at 04:02
  • Quite true that I am avoiding the effects of the original closure. But am I not doing so by creating a new scope with a closure whose effect I like? Remember that the success call back resides inside the immediately invoked function expression I created, causing the "captured" value to be closed over... – JAAulde Aug 05 '11 at 04:06
  • 1
    Very very Thanks, all are helpful!! – blackriderws Aug 05 '11 at 04:13
  • +1 for the first block of code (and point). The second point I will dispute: The div in `$('div#append_result');` slows things down, removing the 'div' part was needed rather than caching the result and using it in the success callback. `$("#append_result")` is negligibly slower than `document.getElementById("append_result")` so I don't think any caching would be necessary in this particular solution. – karim79 Aug 05 '11 at 04:15
  • @karim79 Thanks for the +1. I agree that the `div` portion of the selector should be removed. However, caching the results of `document.getElementById()` is surely more efficient than calling it 5+ times, is it not? So it seems the same would go for the jQuery version. – JAAulde Aug 05 '11 at 04:17
  • @JAAulde - yes, you are creating another closure but that is a side effect, you aren't *using* the closure, you are avoiding the effects of the original closure (and adding another useless variable object to the listener's scope chain, which may or may not be an issue). – RobG Aug 05 '11 at 06:32
3

The Ajax request is asynchronous, which means by the time the success handler returns, the loop is already completed. Instead, you can create a closure to preserve the value:

success: (function(i){
    return function() {
         $('div#append_result').append(i + ',<br/>');
    }
})(initval)
Evan Trimboli
  • 29,900
  • 6
  • 45
  • 66
  • 1
    That isn't a closure, it's avoiding the effects of a closure. The anonymous function still has a closure to initval. – RobG Aug 05 '11 at 04:03
  • @RobG - as I said in reply to the comment on my own answer, I respectfully disagree. – JAAulde Aug 05 '11 at 04:14
  • @JAAulde - actually we agree. However, the extra closure is a side effect, not the reason the original closure is broken. In other words, the fact that it now works isn't because of the extra closure but because of passing the value to another function (and only because the value is a primitive). – RobG Aug 05 '11 at 06:34
2

This is because of the async behavior of ajax: Here is a modified version:

var initval = 1;
var endval = 5;
function action(){
    var action_string = 'txtuser=someone';
    $.ajax({
        type: "POST",
        url: "http://localhost/js.php",
        data: action_string,
        success: function(result){
           $('div#append_result').append(initval + ',<br/>');
           initval++;
           if(initval<=endval)
            action();
        }  
    });
 }

This is now somewhat a sequential approach. Note: I assumed every ajax request returns success, if there is an error, you should handle them on the error callback.

jerjer
  • 8,694
  • 30
  • 36