0

I have a function in JS contains a loop, that calls an AJAX call every iteration. The call to inserts checked elements into a DB and returns the results of those elements in the same page in the next section.

The problem I have is that when I check for e.g. 4 checkboxes out of 3 groupes, the only checkboxes of the last group gets added to the page. However, when I use alert(), I can see all elements.

I used setTimeout, but I got error in the code. I also added lines to give more time to AJX call, but the problem remains. So I wonder if there is a solution to slow down the code without using alert().

This is my script:

addAptitudeField : function(currentAutocompleteField, idChamp) {

    var currentAutocompleteFieldBind = currentAutocompleteField;
    var idChampBind = idChamp;

    window.setTimeout(function() {

        // Code ...

        var paramDwr = {};
        var newDivName = "div" + idChamp + lastValueId;
        paramDwr[attributs.r_divId] = newDivName;
        paramDwr[attributs.r_currentValue] = currentValue;
        paramDwr[attributs.r_hiddenIdsField] = hiddenIdsField.id;
        paramDwr[attributs.r_lastValueId] = lastValueId;
        paramDwr[attributs.r_itemmod] = nbAptitudesCat % 2 == 0;

        // setTimeout ( RepertoireDwr.ligneSuppEtSpanMessage, 1000 ) doesn't work

        RepertoireDwr.ligneSuppEtSpanMessage(paramDwr, function(ajaxPage) {
            divCategorie.update(divCategorie.innerHTML + ajaxPage.texte);
            aptitudeAvecDetail.remetsValeursStockees();
            var btnSuppression = $(newDivName).getElementsByTagName('img')[0];
            btnSuppression.setAttribute("onclick", "formulaireFiche.updateCSS('" + newDivName + "');" + btnSuppression.getAttribute("onclick") + "fiche.updateCategorieSuppressionAptLieeUo(\'divCat" + currentCategorie + "\');"); });
            }
        //
        // alert() : It works in this case.
        //

        // for (var i=0; i<5000000; i++) ; it doesn't work

        }, 400);
    }

Thank you in advance for your help and time.

Martijn
  • 14,522
  • 4
  • 29
  • 61
Chinovski
  • 437
  • 2
  • 7
  • 16
  • What loop? How does it "not work"? What error are you getting? It's really not clear what you're asking, but it definitely sounds like you're trying to solve a problem by creating a different problem. – David May 23 '16 at 18:58
  • I´ve salvaged your question to what I think you mean. Please check out my corrections – Martijn May 23 '16 at 19:02
  • Thank you very much @Martijn, yeap, it is exactly what I am asking for, I am sorry for my language. – Chinovski May 23 '16 at 19:03
  • @David Well, normally, the previous function "addAptitudeField" allows to treat one Element by time, so I create a function that calls this function : `additems : function () { var nbritem = $('selector').length; for (var i = 0; i < nbritem ; i++) addAptitudeField($('selector').[i]); }` It's like this, I call everytime the same function with a particular Element id – Chinovski May 23 '16 at 19:06
  • 1
    you should look into promises. – Craicerjack May 23 '16 at 19:08

3 Answers3

2

I will likely be downvoted for mentioning this, because it is not a recommended procedure, but I believe every coder should have all facts.

In jQuery AJAX construct, there is option async:false, which will delay the script from continuing UNTIL the AJAX has completed processing. Needless to say, if things go wrong in the AJAX the browser could freeze. A lot depends on who your users are, and amount of traffic -- on a few of my ten-user in-house projects it was an acceptable solution.

$.ajax({
    async: false,
    type: 'post',
     url: 'ajax/ax.php',
    data: 'request=',
    success: function(d){
        if (d.length) alert(d);
    }
});

Ref:

What does "async: false" do in jQuery.ajax()?


The better idea, however, is to look into the Promises interface, with methods like .when() and .then()

References:

https://jsfiddle.net/v86bc028/2/

http://jqfundamentals.com/chapter/ajax-deferreds#

http://digitizor.com/jquery-html-callback-function-using-promise/#

how does jquery's promise method really work?

Community
  • 1
  • 1
cssyphus
  • 31,599
  • 16
  • 79
  • 97
1

The problem you're running into deals with asynchronous functions, or the A in AJAX. If you don't know what an asynchronous function is, there are many others who can explain it better than I can, so give that a google.

What's happening without the alert() in there is your code makes 4 sever calls, but all 4 get sent out before you get a response to any of them. With the alert() (or setTimeout), you're giving the code time to received each response to a call before the next one is made.

There are several ways you can approach this, the first way is by calling the next call after the first receives a response. The second way is to use an async function to call all 4 at once on different chains(?). I'm not the best at explaining this part, but there's plenty of code to be found on SO and online.

Lucas Watson
  • 663
  • 7
  • 24
  • Yeap, that is exactly what I am trying to do, is to proceed the next call after the the first receives a response, by using setTimeout and many instructions to give time to Ajax, but no good results. Thank you by the way – Chinovski May 23 '16 at 19:12
1

I think you have a more generic problem in your code, since you seem to need to delay your executions to wait till sth. else is finished, instead of getting anounced when it is done.

The line that annoys me most is this one

divCategorie.update(divCategorie.innerHTML + ajaxPage.texte);

what exactly is update doing? How is it implemented? I assume it does sth. like divCategorie.innerHTML += ajaxPage.texte;
Wich is highly unfavorable, since the browser has to parse and rebuild, whatever there already is in divCategorie.innerHTML.

Just appending the new Markup would be better.

long way short: maybe a good hack would be to insert some hidden node as a placeholder (so you kan keep order, although the AJAX-requests may return in a different order) and replace that node with the real content, as soon as it arrives.

Kind of like this:

addAptitudeField : function(currentAutocompleteField, idChamp) {

    var currentAutocompleteFieldBind = currentAutocompleteField;
    var idChampBind = idChamp;

    //this is done immediately, and therefore preserves the order of the loop, 
    //without any delays/timeouts
    var placeholder = document.createElement("div");
        placeholder.className = "placeholder";
        placeholder.style.display = "none";
    divCategorie.appendChild(placeholder);

    window.setTimeout(function() {

        // Code ...

        var paramDwr = {};
        var newDivName = "div" + idChamp + lastValueId;
        paramDwr[attributs.r_divId] = newDivName;
        paramDwr[attributs.r_currentValue] = currentValue;
        paramDwr[attributs.r_hiddenIdsField] = hiddenIdsField.id;
        paramDwr[attributs.r_lastValueId] = lastValueId;
        paramDwr[attributs.r_itemmod] = nbAptitudesCat % 2 == 0;

        // setTimeout ( RepertoireDwr.ligneSuppEtSpanMessage, 1000 ) doesn't work

        RepertoireDwr.ligneSuppEtSpanMessage(paramDwr, function(ajaxPage) {
            //convert the passed text into a DocumentFragment
            var frag = fragment(ajaxPage.texte);

            //replacing the placeholder with the fragment
            divCategorie.insertBefore(frag, placeholder);
            divCategorie.removeChild(placeholder);

            aptitudeAvecDetail.remetsValeursStockees();
            var btnSuppression = $(newDivName).getElementsByTagName('img')[0];
            //this is also pretty horrible to me:
            btnSuppression.setAttribute("onclick", "formulaireFiche.updateCSS('" + newDivName + "');" + btnSuppression.getAttribute("onclick") + "fiche.updateCategorieSuppressionAptLieeUo(\'divCat" + currentCategorie + "\');"); });
            }
        }, 400);
    }

I think you should do some major refactoring. And take a look into Promises.

// * -> DocumentFragment
//strings/primitives are parsed as HTML-markup, 
//null / undefined is ignored
//Arraylike structures are parsed recursively
var fragment = (function(container){
    return function(src){
        return reducer(document.createDocumentFragment(), src);
    }

    function reducer(frag, node){
        var i, len, fc, c, r;
        if(node === Object(node)){
            if("nodeType" in node){
                //dom nodes
                frag.appendChild(node);
            }else{
                //Arraylike structures, like NodeLists or jQuery-Objects, or just plain Arrays
                for(i = 0, len = ("length" in node && node.length)|0, r = reducer; i < len; (i in node) && r(frag, node[i]));
            }
        }else if(node != null) {
            //strings (all primitives)
            for((c=container).innerHTML = node; fc = c.firstChild; frag.appendChild(fc));
        }
        return frag;
    }
})(document.createElement("div"));
Thomas
  • 3,245
  • 1
  • 10
  • 8
  • Thank you for your reply, firstly, the update function allows to show what categories was already added to the field, so everytime I add a new category this function insert it to the old list. I admit the code is not clear, it is old and complicated for me (I am a beginner in JS). I will try with your code and see what I can do with it. Thank you again. – Chinovski May 23 '16 at 20:55