setTimeout not executed in loop(each)

Asked

Viewed 528 times

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);
});

Anyway, group is an array with about 5 groups.

The idea is that each time you go through the array(group[0],group[1]..) has a 10-second interval, but what happens is different, it waits 10 seconds to start, but sends all AJAX requests, that is, it waits 10 seconds, and then go through the whole array and send ajax, it’s as if from the second time the setTimeout were ignored..

In my thought, I should wait 10 seconds (10000ms) and then send ajax, then go to the next input of the array(group), wait 10 and go ajax, and so on until I go through completely..

2 answers

5


Contrary to what you’re thinking, the whole loop of each occurs immediately. Only what is within the "delayed" wheel setTimeout, asynchronously (as if it were "parallel"). Inside the loop, your code doesn’t stop for 10 seconds.

The quickest fix is to use 10000 * i in the setting of timers. This delays each one by 10 seconds from the previous one. It’s not very accurate, but in your case there doesn’t seem to be any need for more than that.


Another solution a little more elegant (but you can still 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 the DOM change (the append in your table with the result of each request).

  • 1

    More details: http://answall.com/questions/51268/qual-a-diferença-entre-comunicação-ass%C3%Adncrona-e-s%C3%Adncrona/51269#51269, http://answall.com/questions/16950/como-programming-ass%C3%Adncrona-funciona-em-javascript

  • Bah! I thought that setTimeout locks the script by the time I estimated until the processing of the function it contained, as php Sleep() does.

  • but it wouldn’t make sense from there, because it would compromise the rest of the script that comes later, but his job is just to estimate a time, delay the function. Valeu @bfavaretto!

  • 1

    It’s not like that. Ajax isn’t like that either, it’s the same principle n

  • Look at that, before I add the *i in the 10000, was sending right the group array: grupo = ["951179678239911","1559692210955385","860177374023863","722424877870676"], now, I added this (only this change), and at some point the group[i](in the second) just returning me the value 1, and the 4(last) I just return 9, then when I take the *i indices correspond to the value right, this can be by what? for me makes no less sense, in fact I find nothing to even see..

  • the interesting thing is that, although after the 9511 came 1559 the order that happens is 9511.. then comes 1, then 1559.. after last 9. that is, the indices of 860177.. and 72242.. are not sent with the correct value and still outside the array order..

  • You change this array of groups somewhere after this code?

  • change no, just send to curltst.php the group by the POST method using AJAX, and there in curltst.php I do the procedures with the group, it happens that in the request what is being posted is of disagreement with what was to be, the values of the array is correct(as I showed above, it is exactly what is in the group array), it was to be 72242.. and it’s going 9, now I tested again with the team 10000*i(before it was 30000*i) and sometimes not even send the group(as if the group[i] was empty)..

  • 1

    I don’t know what it could be. See a proof of concept that this structure works.

  • according @bfavaretto, great response, must be something then that went unnoticed, obg by the attention!

  • Well, I’ve included another way, if you want to test.

  • 2

    By the way, I found the cause of your problem: you redefine grupo within the Success, a var there.

  • perfect @bfavaretto, that was exactly the mistake, now everything is ok, thanks for the force, I was going to skate in it a while because this variable of global and local had not even passed in the head.. all the best for you there master! :)

  • 2

    This solution does not seem right to me. what I understand is that @Alexandrec.Caus wants to make the requests after 10 seconds and not put the results in the DOM after 10 seconds. Where in the case is making all requests immediately but waiting 10 seconds to persist.

  • 2

    @Gabriel it is true that the code above (which was my second suggestion to him) works as you said. But I’m not so sure that this would be a problem for what it wants. It might be, it might not be. In fact I missed explaining what the code does.

  • 2

    I understand the motivation and agree with your answer, but aiming at the best learning I made an answer that exactly matches the result expected by the question. Unfortunately it’s more complex, but I think it can help other people who have the same question but are not satisfied with your answer which is specific to the @Alexandrec.Caus user

  • 2

    Great @Gabrielgartz, +1 in your answer. In fact it’s the "right" way to do it. You surmised well, I went the easy way for considering that it would be easier for Alexandre to understand.

Show 12 more comments

4

Knowing @bfavaretto I know that he used a didactic way to solve the problem and explain in a simple way how JS works. I will try to be a little more technical here I directly answer the question of the user @Alexandre C.Caus.

Javascript runs on only one thread, but functions as setTimeout will be executed late without stopping the execution of the function that executed the call. That is, the loop will be fully executed and the setTimeout shall be lined up after the execution, awaiting the time of the point at which they were called.

For a code to be called after 10 seconds in sequence, the setTimeout should be "within" of the execution of the previous.

Because this process generates many functions within each other, the code can get very messy, to simplify a little the answer I will employ the use of jQuery’s own promises, 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 execution flow of this code:

  • variable for promises is defined in scope
  • Loop is started
    • check if there are promises, or do the prepares the request and save promise (the request will be executed later), goes to the next loop result;
    • if there’s promise, prepares setTimeout to return a promise that contains the AJAX request and puts it in the promise queue, then goes to the next loop result;
  • Finished the loop;
  • First AJAX request executed, when return;
  • Initiating setTimeout 10 seconds, when finished;
  • Start next AJAX request, and when finished, repeat the process of the previous step until all promises are resolved;
  • interesting that of promises, I will read more on this to iterate me of its resolution!

  • then the deferred object it will contain the entire list for execution, in case all AJAX requests will be contained in it, but they will run as it ends another, like, when the promise of the previous one is made, is that it? for me this is a very new concept of treating asynchronous requisicoes, before I put everything in Success since it assures me that the requisition has already been made

  • this line here: ajaxCall(i).done(deferred.resolve).fail(deferred.fail);, is like, when the function is performed(the request has been made) it informs the deferred that it has been resolved or when it fails that there has been failure, then, if resolved, it goes to the next execution(if there is any)?

  • 1

    Straight in the comment will be difficult to explain to you But what he is doing there is creating a row of "callbacks", in the loop, I am queuing, and when finishing the loop the first $.ajax will start and when it is finished, will fire the next using then and so on and on until.

  • 1

    every command ajax of jquery returns a promisse so I can use straight the way I did. The success callback is the same as done of the promise and the fail It has the same name for Prefects or by callback. What I in the line you commented is, if the ajax returns success, resolves the promise, if it returns failure, fails the promise. This way I can create the promise, before creating the timeout and just return the result of the promise when finishing the ajax request (no matter how long the ajax takes).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.