1

Setup


I have a form that uses a series of check boxes to determine a number selected on a page:

math on checkboxes here is correct. should be 4

What it does is on selected, it makes a call off to a controller that will save the selection and return the new checked amount based on its current data.

The code looks like this


$('#myId').('click', '.selectorClass input', function() {
    const inputCheckBox = this;
    inputCheckBox.disabled = true;
    $(inputCheckBox).addClass('disabled-checkbox')

    $.ajax({ 
       method: "POST",
       url: "@(Url.Action<MyController>(c => c.MyEndPoint(Model.ItemId, null)))",
       data: {
          "boxWasChecked": inputCheckBox.checked
       }
    }).done(function (data) {
       UpdateFields(inputCheckBox, data); //this would update the column footer and 'Total Quantity' field below
    }).fail(function (data) {
       // error check and handle here
    }).always(function(data) {
       inputCheckBox.disabled = false;
       $(inputCheckBox).removeClass('disabled-checkbox')
    });
});

This works great when selecting one at a time and even usually works when selecting multiple.


Problem


The issue comes when a user selects them very quickly and then sometimes the last request to finish wasn't the last one to come in/grab the data from the database on the controller side and therefore it brings back the wrong math for the total.

See image below where the math should be 11 (remember that it is adding the value of each box rather than number of boxes checked) but is reporting 6.
If I were to select the final check box, it would correctly report 12 because it has all the correct data in place for the call.

incorrect math for checkboxes. should be 11


Part where I am stuck


I think I need to somehow make a list of ajax calls and then only call off a request if there is a request that is added after it OR all other requests infront of it have been completed. I don't know how to do this with ajax calls.

I have toyed around with promises/Fetch API and tried to get that working but could get the promise to add to the stack in Promise.all[myListOfAjaxCalls].

Summary of Code I am working towards

var listOfPromiseCalls = []

$('#myId').('click', '.selectorClass input', function() {
     // my checkbox is clicked
    listOfPromiseCalls.push(myAjaxCall/myPromise)
    // Psudeo code   
    // begin request if it is the only one in the list
    // else
        // kick off other requests that are in existing list infront of it
    // if other requests finish before another input is clicked, begin this request
});
Community
  • 1
  • 1
thalacker
  • 2,389
  • 3
  • 23
  • 44

1 Answers1

2

I think you are in the right path by creating a list with all requests, but since you are using jQuery to do async call, you should use the $.when operator to await until all requests are finished. Promise.all woun't work because $.ajax doesn't implements the same interface as a new Promise() does, so you need to handle it using the jQuery operator.

You can use a list with all async calls as you described above, and then do something like:

$.when(...listOfPromiseCalls)
  .done((...allResponses) => {
    // do something 
  })
  .catch(errors => {
    // handle errors
  });

Notice that I'm using spread operators from es6 to spread all requests to when and also to aggregate all responses in a list of responses.

About the spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

If you can't use es6 syntax, I recommend you to study and use the apply method for js functions, which gives you the same result.

Marco Antônio
  • 880
  • 8
  • 15
  • Thanks for your answer! I do not need to go with `ajax` if you believe `promise` would work better. Anyway, with your answer, how would I call this `$.when()` in relation to my current ajax call? – thalacker Feb 11 '20 at 20:15
  • also, looks like maybe Promises and ajax do work together https://stackoverflow.com/a/14038050/10177987 – thalacker Feb 11 '20 at 22:45
  • To use `Promise.all` with ajax, we have to transform an ajax call to promise, like: `const ajaxToPromise = (ajaxCall) => new Promise((resolve, reject) => ajaxCall.done(resolve).catch(reject))` and then do something like `Promise.all(listOfPromiseCalls.map(ajaxToPromise))` – Marco Antônio Feb 12 '20 at 00:22
  • 1
    Cool cool. How can I queue up that list with the inputs though? Perhaps using a timer or something along those lines to execute the list of promiseCalls every second or something? – thalacker Feb 12 '20 at 17:59
  • 1
    Because the ajax call above would be immediately executed – thalacker Feb 12 '20 at 18:52
  • I see, you can just have a list of the ajax call params, then you map it to `$.ajax`. Something like this `[ { method: 'GET', url: 'bla' }, { method: 'GET', url: 'bla' } ].map($.ajax)` – Marco Antônio Feb 14 '20 at 14:01