How to improve code workflow without using synchronous ajax?

Asked

Viewed 438 times

1

How to improve this script?

The function copiaArea() should take the first return of the array and send to the windows clipboard, detail: I am developing for IE, works exclusively in it this function. But the global var numero, does not receive on time on the second call from listaNumeros(), after the adicionarNúmero()(generated by Trigger in the BD, for date processing and direct numbering sequence in mysql.).

The question is as follows, the contraindications of using the async:false in ajax are relevant here?

Another detail, is that this only runs on local network server, does not depend on the internet.

/*
* Loader da numeração
* @author marcelo aymone
*/

$(document).ajaxStart(function() {
    $('#loading').show();
});

$(document).ajaxStop(function() {
    $('#loading').hide();
});

var ip;
var numero;

function inserirLinha(array) {
    var linha = $("<tr>");
    var col = "";
    col += "<td>" + array.num + "</td>";
    col += "<td>" + array.time + "</td>";
    linha.append(col);
    if (array.ip == ip) {
        linha.toggleClass("info");
    }
    $("#numeros").append(linha);
}

function sysMsg(msg) {
    $("#alertas").empty().append(msg);
}

function copiaArea(txt) {
    if (window.clipboardData && clipboardData.setData) {
        clipboardData.setData('text', txt);
    }
}

function atualizaTabela(data) {
    var msg = "";
    if (data.status) {
        msg = "<div class=\"alert alert-success text-center\">";
        msg += "<h4>Sucesso!</h4>";
        msg += "Seu número foi gerado com sucesso!";
        msg += "</div>";
    } else {
        msg = "<div class=\"alert alert-error\">";
        msg += "<h4>Sucesso!</h4>";
        msg += "Ocorreu um erro e não foi possível gerar um número.";
        msg += "</div>";
    }
    $("#numeros").empty();
    listarNumeros();
    sysMsg(msg);
}

/*
* ListarNumeros recebe o seguinte json:
* {"lista":[{"num":"2014\/1618","ip":"10.120.2.35","time":"15\/05\/2014"}],"cliente":"10.120.2.35"}
**Possui \/ por causa do json_encode(), mas funciona ok.
*/

function listarNumeros() {
    $.ajaxSetup({cache: false, async:false});
    $.getJSON("/intracake/Numeros/listar.json")
            .done(function(data) {
                ip = data.cliente;
                numero = data.lista[0].num; //Pega o 1 resultado do array, último num gerado.
                $.each(data.lista, function(index, array) {
                    inserirLinha(array);
                });
            });
}
/*
* Recebe como retorno do json: {"status":true}
*/
function adicionarNumero() {
    $.post("/intracake/Numeros/adicionar.json")
            .done(function(data) {
                atualizaTabela(data);
                copiaArea(numero);
            });
}

$(document).ready(function() {
    listarNumeros();
});

Html+php script (cakephp):

<?php
 echo $this->Html->script('numeros-run', array('inline' => false));
?>
<style>
    .centralizado {
        margin: 0 auto !important;
        float: none !important;
    }
    .table td, .table th {
        text-align: center;   
    }
</style>

<div class="span10">
    <div id="alertas"></div>
    <p class="text-center"><button class="btn btn-large btn-primary" type="button" onclick="adicionarNumero();">Gerar número!</button></p>
    <div id="loading" class="text-center"><p><img src="img/ajax-loader.gif"></p></div>
    <p class="text-center lead">Últimos números gerados:</p>
    <div class="span4 centralizado">
        <table id="numeros" class="table table-bordered">
        </table>
    </div>
    <p class="text-center">
        <i class="icon-info-sign"></i>
        Todos números gerados pelo seu ip, 
        aparecem destacados em azul.
    </p>
    <p class="text-center">
        <i class="icon-info-sign"></i>
        Ao gerar um número, ele é enviado para a área de transferência do windows.<br>
        <i class="icon-info-sign"></i>
        Compatível apenas com Microsoft Internet Explorer.<br>
        <strong class="text-error">Você precisa aceitar na janela que abrirá</strong>, para que o número possa ser copiado automaticamente.<br>
        Depois basta você colar("ctrl+v") em qualquer lugar.
    </p>
</div>
  • 1

    Yes, it can stop everything. What you need is a synchronization scheme (created by you, not by jQuery) using a global variable, for example var estado = 0; When you call one of the two functions, add 1 to the variable, and call a third function executaFim(). Within executaFim(), you validate if estado is worth 2. If yes, execute your code that depends on the two other functions have performed. Otherwise, do nothing.

  • @carlosrafaelgn, isn’t that the same? because if you put a condition that allows you to not perform anything, it is the same result of ajax having no return and keep the page static.

  • 1

    No, it’s not the same, no. Because, despite "doing nothing", at least it didn’t catch the user screen, while the answer doesn’t arrive ;)

  • I get it... but I don’t want to do this so that it looks like a gambiarra, I’d like to set in the callback’s running stack pattern.

  • If I add the callback .fail({return false;}) as callback of the ajax request itself, will not "unlock" the page and continue the execution?

  • That’s not "gambiarra". That’s kind of state machine very simple.

  • Where the function is called adicionarNumero?

  • "Crash" is not due to error or success, but rather due to the delay of the data reaching the customer’s machine (be it correct or wrong data)

  • I get it, I’ll read the link... and try to understand how to implement, thanks @carlosrafaelgn

  • @bfavaretto: No html, single button onclick. <button class="btn btn-large btn-primary" type="button" onclick="adicionarNumero();">Gerar número!</button>

  • The link is just to clarify that is not gambiarra do what I said (use the variable estado). You don’t need to read it all just to implement this suggestion, in case you’re in a hurry ;)

  • Yeah, I get it, I’m not in a hurry, I’m in it for the learning! Actually I take care of the infra... but you know how it is right... we always search the neighbor’s grass.

  • @bfavaretto added the HTML, repairs not the lot of code, it is because using cakephp, twitter bootstrap and the requests in ajax format with jquery.

  • @carlosrafaelgn I just didn’t understand the following, if (estado ==1) after the execution of the script, even though it still doesn’t work, it is running in a for to realize that estado was changed? How to establish this "Systener" to verify that estado was changed? I tried to find some example on the net.. but only very complex scripts, being part of long applications.

  • I thought I would use something like this: http://www.douglaspasqua.com/2013/08/14/javascript-e-funcoes-de-retorno-callback/

  • Try to see if the answer given was what you wanted, or take a look at this fiddle I just created (lines marked with @@@ are the ones I created): http://jsfiddle.net/USnmL/1/

Show 11 more comments

2 answers

5


If I understand correctly, the code stream posted, with the use of asynchronous ajax, is as follows:

  1. Button clicked, adicionarNumero is called.
  2. JSON (adicionar.json) received, atualizaTabela is called.
  3. atualizaTabela flame listarNumeros.
  4. copiaArea is called, passing the previous value of numero for the answer of listar.json has not yet arrived.
  5. The answer comes from listar.json and the global numero is updated (too late!)

Solution

Whereas you don’t want to call copiaArea whenever listarNumeros is executed, and yes only after the adicionarNumero, I suggest taking advantage of the Promises returned by jQuery ajax methods, thus:

function atualizaTabela(data) {
    var msg = "";
    if (data.status) {
        msg = "<div class=\"alert alert-success text-center\">";
        msg += "<h4>Sucesso!</h4>";
        msg += "Seu número foi gerado com sucesso!";
        msg += "</div>";
    } else {
        msg = "<div class=\"alert alert-error\">";
        msg += "<h4>Sucesso!</h4>";
        msg += "Ocorreu um erro e não foi possível gerar um número.";
        msg += "</div>";
    }
    $("#numeros").empty();
    return listarNumeros();
    sysMsg(msg);
}

function listarNumeros() {
    $.ajaxSetup({cache: false, async:false});
    return $.getJSON("/intracake/Numeros/listar.json").done(function(data) {
        ip = data.cliente;
        numero = data.lista[0].num; //Pega o 1 resultado do array, último num gerado.
        $.each(data.lista, function(index, array) {
            inserirLinha(array);
        });
    });
}

function adicionarNumero() {
    $.post("/intracake/Numeros/adicionar.json").done(function(data) {
        atualizaTabela(data).done(function(){
            copiaArea(numero);
        });
    });
}

Note: the solution below was proposed when one of the requirements was not clear to me

It seems to me that to solve the problem it would be sufficient to withdraw the call from copiaArea of function adicionarNumero, and move on to listarNumeros. With this, you can even get rid of the global variable (about this, see Why using global variables is not a good practice?). That is to say:

function listarNumeros() {
    $.ajaxSetup({cache: false, async:false});
    $.getJSON("/intracake/Numeros/listar.json")
            .done(function(data) {
                ip = data.cliente;
                // remover a linha abaixo
                //numero = data.lista[0].num; //Pega o 1 resultado do array, último num gerado.

                // inserir a seguinte linha:
                copiaArea(data.lista[0].num);

                $.each(data.lista, function(index, array) {
                    inserirLinha(array);
                });
            });
}

function adicionarNumero() {
    $.post("/intracake/Numeros/adicionar.json")
            .done(function(data) {
                atualizaTabela(data);
                // remover a linha abaixo
                //copiaArea(numero);
            });
}
  • You are ninja. It is not the first time you help me here in the stack... I will test and I already answer.

  • 1

    If I understood the question correctly, it was not to make the copy only the second time the function listarNumeros() be called?

  • @carlosrafaelgn Xi, then I don’t know, let’s wait for the author to clarify this.

  • @Marceloaymone Thanks for the compliment, but it was nothing ninja to get to that answer :) I had to focus a while on your code until I understood this flow...

  • But then, bfava, I didn’t want it to trigger the window.clipboardData object in the first listing, only after the click, because the script lists the previously generated numbers, but it should trigger the Clipboard only after the call of adicionaNumeros() because the Clipboard model opens when starting the copy.

  • Exactly @carlosrafaelgn, well noted!

  • 1

    Take a look at my fiddle, @Marceloaymone, and see if that’s it (lines marked with @@@ are the ones I created): http://jsfiddle.net/USnmL/1/

  • Hummm Now I get it!! You’re also ninja @carlosrafaelgn!!

  • Shame on my face, calling your "like Pattern" solution gambiarra. hauhaeueauh Cara is a teacher at ITA. Thanks Master!!

  • Hi @Marceloaymone! So, I’m not a teacher there, no. I graduated there :) Now... I don’t know the "like Pattern"...

  • Sorry, slang mania... because the Finite state machine, or finite state machine would be a standard encoding for certain problems, like this.

  • Now I get it, @Marceloaymone. I updated my answer with another solution, which I think is more "asynchronous style" than using status flags.

  • I was looking at some publications that explore the papers as you have already mentioned, I was looking for this type of solution... I will test here.

  • @bfavaretto It worked perfectly!!! ;)

Show 9 more comments

3

You can use a global counter and a Guard condition to synchronize the execution of its asynchronous actions. More or less as it is done in a State machine.

In your code, this would look like this (only modifications):

Stretch 1:

var estado = 0;

function executaFim() {
    if (estado >= 3)
        copiaArea(numero);
}

Stretch 2:

function listarNumeros() {
    $.ajaxSetup({cache: false, async:false});
    $.getJSON("/intracake/Numeros/listar.json")
            .done(function(data) {
                // seu código original
                estado++;
                executaFim();
            });
}

Section 3:

function adicionarNumero() {
    $.post("/intracake/Numeros/adicionar.json")
            .done(function(data) {
                // seu código original
                estado++;
                executaFim();
            });
}

Browser other questions tagged

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