setTimeout not executed in the loop (each)

2

See the function below:

$(grupo).each(function (i) {
    setTimeout(function () {
        $.ajax({
            url: "curltst.php",
            data: {
                acao: "teste",
                mensagem: mensagem,
                grupo: grupo[i]
            },
            dataType: "xml",
            method: "POST",
            success: function (data) {
                grupo = $(data).find("tdgrupo").text();
                status = $(data).find("tdstatus").text();
                $("tbody").append("
<tr>
    <td>" + grupo + "</td>
    <td>" + status + "</td>
</tr>");
            },
            error: function () {
                alert("ERRO 008");
            }
        });
    }, 10000);
});

At last, group is an array with some 5 groups.

The idea is that every time you go through the array (group [0], group [1] ..) has an interval of 10 seconds, but what happens is different, it waits 10 seconds to start, but it sends all the AJAX requests, that is, it waits for 10 seconds, and then it runs through the array and sends ajax, it is as if from the second time the setTimeout is ignored ..

In my thinking, you should wait 10 seconds (10000ms) and then send ajax, then go to the next array index, wait 10 and go ajax, and so on.

    
asked by anonymous 20.03.2015 / 01:06

2 answers

5

Contrary to what you're thinking, the entire each loop occurs immediately. Only what is inside the setTimeout runs "backward," asynchronously (as if it were "in parallel"). Inside the loop, your code will not stop for 10 seconds.

The fastest fix is to use 10000 * i in setting the timers. This delays each in 10 seconds from the previous one. Not very accurate, but in your case there seems to be need for more than that.

Another solution is a bit more elegant (but still can improve):

$(grupo).each(function (i) {
    $.ajax({
        url: "curltst.php",
        data: {
            acao: "teste",
            mensagem: mensagem,
            grupo: grupo[i]
        },
        dataType: "xml",
        method: "POST",
        success: function (data) {
            setTimeout(function () {
                var grupo = $(data).find("tdgrupo").text();
                var status = $(data).find("tdstatus").text();
                $("tbody").append("<tr><td>" + grupo + "</td><td>" + status + "</td></tr>");
            }, 10000 * i);
        },
        error: function () {
            alert("ERRO 008");
        }
    });
});

Important: This solution executes all ajax calls immediately, and only delays DOM change (the append in your table with the result of each request).

    
20.03.2015 / 01:17
4

Knowing @bfavaretto I know he used a didactic way to solve the problem and explain in a simple way how JS works. I'll try to be a bit more technical here, I'll answer the question directly from the user @Alexandre C.Caus.

JavaScript runs on just one thread , but functions like setTimeout will run late without stopping the execution of the function that ran the call. That is, the loop will be fully executed and the setTimeout will be queued after execution, waiting for the time from the point where they were called.

For a code to be called after 10 seconds in sequence, the subsequent setTimeout must be "inside" of the previous execution.

Because this process generates many functions one inside the other, the code can get very messy, to simplify the answer a bit I will employ the promises of jQuery itself, to simplify the code:

var promisse;
var waitFor = 10e3; // 10 segundos
$(grupo).each(function(i){
    function ajaxCall(i) {
        return $.ajax({
            url: "curltst.php",
            data: {
                acao: "teste",
                mensagem: mensagem,
                grupo: grupo[i]
            },
            dataType: "xml",
            method: "POST",
            success: function (data) {
                grupo = $(data).find("tdgrupo").text();
                status = $(data).find("tdstatus").text();
                $("tbody").append("
                <tr>
                    <td>" + grupo + "</td>
                    <td>" + status + "</td>
                </tr>");
                },
            error: function () {
                alert("ERRO 008");
            }
        });
    }

    // Na primeira requisição use a promessa diretamente do retorno do
    // método ajax, isso vai fazer com que a primeira requisição seja
    // instantânea.
    if(!promisse) promisse = ajaxCall(i);
    // Caso já seja uma promessa, a função de callback vai ser chamada
    else promise.then(function(){
        // Definimos a deferencia da promessa que irá aguardar o tempo
        // desejado aqui, e já retornamos a nossa sequencia de promessas
        // antes dela ser resolvida, assim o loop segue fazendo o mesmo
        // até chegar ao fim.
        var deferred = $.deferred();
        // Isso será executado apenas quando a promessa anterior for resolvida
        // isso é, você tem o resultado da sua ultima requisição ajax e
        // agora vai esperar o tempo necessário pra proxima requisição
        setTimeout(function(){
            // Passado tempo, agora vamos fazer a nova requisição
            // e vamos fazer uma ponte do resultado da requisição a nossa
            // promessa, assim o proximo só começa ser executado quando
            // a resposta chegar
            ajaxCall(i).done(deferred.resolve).fail(deferred.fail);
        }, waitFor);
        return deferred.promise();
    });
});

How would the flow of this code be:

  • variable for promises is defined in scope
  • Loop starts
    • check for promises, otherwise make the prepare the request and keep promise (the request will be executed later), go to the next loop result;
    • If there is a promise, prepares the setTimeout to return a promise that contains the AJAX request and puts it in the promises queue, then goes to the next loop result;
  • Finished the loop;
  • First AJAX request is executed, when to return;
  • Starts setTimeout 10 seconds when finished;
  • Start the next AJAX request, and when done, repeat the process from the previous step until all promises are resolved;
20.03.2015 / 18:03