How do I wait for a .each loop that makes asynchronous calls?

I have some javascript code that updates some data in a database using an http handler, but this asynchronous call is being made inside a loop .each

. At the end of the loop, I make a call to the function CancelChanges()

that refreshed the page. The problem is that the page seems to be refreshed before the database is updated. The loop .each

seems to end after the call CancelChanges()

. How can I make sure the page is refreshed after all async calls in the .each loop have finished?

function SaveChanges() {
    if (PreSaveValidation()) {
        var allChangesSucceeded = true;
        var studioId = $("#param_studio_id").val();
        var baseDate = $("#param_selected_month").val().substring(6, 10) + $("#param_selected_month").val().substring(0,2);
        var currency = "CAD";
        var vacationPct = null;
        var gvAdmissible = null;

        $(".editable-unsaved").each( function() {
            var newSalary = $(this).text();
            var disciplineId = $(this).data("disciplineid");
            var seniorityId = $(this).data("seniorityid");
            var handlerCommand = "";
            if ($(this).data("valuetype") === "inflated") {
                handlerCommand = "AddAverageSalary";
            } else if ($(this).data("valuetype") === "actual") {
                handlerCommand = "UpdateAverageSalary";
            }
            $.get("WS/AverageSalary.ashx", { command: handlerCommand, studio_id: studioId, discipline_id: disciplineId, seniority_id: seniorityId, base_date: baseDate, currency: currency, salary: newSalary, vacation_pct: vacationPct, gv_admissible: gvAdmissible }).done(function (data) {
                if (data != "1") {
                    $(this).removeClass("editable-unsaved");
                    allChangesSucceeded = true;
                }
                else {
                    alert('fail');
                    allChangesSucceeded = false;
                }
            });
        });
        if(allChangesSucceeded) CancelChanges();
    }
}

function CancelChanges() {
    var href = window.location.href;
    href = href.split('#')[0];
    window.location.href = href;
}

      

+3


source to share


5 answers


I think you need to modify the structure a bit by using a counter and calling CancelChanges when the counter is equal to the number of calls.



function SaveChanges() {
    if (PreSaveValidation()) {
        var studioId = $("#param_studio_id").val();
        var baseDate = $("#param_selected_month").val().substring(6, 10) + $("#param_selected_month").val().substring(0,2);
        var currency = "CAD";
        var vacationPct = null;
        var gvAdmissible = null;

        var editableUnsaveds = $(".editable-unsaved"); //cache the selector here, because selectors are costly
        var numOfGetsReturned = 0;            

        editableUnsaveds.each( function() {
            var newSalary = $(this).text();
            var disciplineId = $(this).data("disciplineid");
            var seniorityId = $(this).data("seniorityid");
            var handlerCommand = "";
            if ($(this).data("valuetype") === "inflated") {
                handlerCommand = "AddAverageSalary";
            } else if ($(this).data("valuetype") === "actual") {
                handlerCommand = "UpdateAverageSalary";
            }
            $.get("WS/AverageSalary.ashx", { command: handlerCommand, studio_id: studioId, discipline_id: disciplineId, seniority_id: seniorityId, base_date: baseDate, currency: currency, salary: newSalary, vacation_pct: vacationPct, gv_admissible: gvAdmissible }).done(function (data) {
                if (data != "1") {
                    $(this).removeClass("editable-unsaved");
                }
                else {
                    alert('fail');
                }
                if(editableUnsaveds.length === ++numOfGetsReturned){
                   CancelChanges(); //now it should call when the final get call finishes.
                }
            });
        });
    }
}

function CancelChanges() {
    var href = window.location.href;
    href = href.split('#')[0];
    window.location.href = href;
}

      

+1


source


You can try using Promises and jQuery$.when

Keep a list of promises ajax calls:



var defereds = [];

$(".editable-unsaved").each( function() {
    //...
    defereds.push($.get("WS/AverageSalary.ashx" /*...*/));
}
$.when.apply($, defereds).done(function() {
    CancelChanges();
});

      

This should hopefully wait for all ajax calls to complete before calling CancelChanges()

+6


source


I would use promises . q library is my favorite way to implement them. But since you are using JQuery, I would recommend following a similar approach to what I describe below, but using $.when

insteadq.allSettled

I often use promises when clearing multiple sites at once - I need to iterate over a long list of websites, make requests for content, and do something with the content when requests are returned. The last thing I want to do is send requests one at a time, processing each one when it returns.

In the abstract, it looks like this:

function scrapeFromMany() {
  var promises = [];

    _.forEach(urls, function(url) {

      // this makes the request
      var promise = scraper(url);

      // this stores the promise with the others you iterate through
      promises.push(promise); 
    });

  q.allSettled(promises).then(function(res) {
    // this function is executed when all of the promises (requests) have been resolved 

   console.log("Everything is done -- do something with the results.", res);
  });
}

      

Fwiw, promises are not easy to twist if you've never used them. If this happens, plan to spend some time to speed up with concepts. They will change (much better) the way you write javascript async and they really are a blessed way with these kinds of operations.

+1


source


Asynchronously call the test function in the "done" function handler. Keep track of how many requests were completed and process only once, equal to the total number of expected requests.

if (PreSaveValidation()) {
    var allChangesSucceeded = true;
    var length = $(".editable-unsaved").length;
    var completedCount = 0;
    // ...

    $(".editable-unsaved").each( function() {
        // ...
        $.get("WS/AverageSalary.ashx", data).done(function (data) {

            completedCount++;

            if (data != "1") {
                $(this).removeClass("editable-unsaved");
                // don't set all changes succeeded to true here
            }
            else {
                alert('fail');
                allChangesSucceeded = false;
            }

            isComplete(length, completedCount, allChangesSucceeded);
        });
    });
}

function isComplete(totalLength, currentLength, allChangesSucceeded) {
    if (currentLength == totalLength) {

        // should this be !allChangesSucceeded?
        if (allChangesSucceeded) CancelChanges();
    }
}

      

0


source


This is because you do not wait for the requests to complete to continue the loop. To do this, you need to set the "async" flag to false. The server call should be as follows:

    $ .ajax ({
        url: "WS / AverageSalary.ashx",
        async: false,
        data: {command: handlerCommand, studio_id: studioId, discipline_id: disciplineId, seniority_id: seniorityId, base_date: baseDate, currency: currency, salary: newSalary, vacation_pct: vacationPct, gv_admissible: gvAdmissible},
        success: function (data) {
            if (data! = "1") {
                $ (this) .removeClass ("editable-unsaved");
                allChangesSucceeded = true;
            }
            else {
                alert ('fail');
                allChangesSucceeded = false;
            }
        }
    });

0


source







All Articles