3

I need to do the opposite of this post, "Best way to iterate over an array without blocking the UI"

I have to loop through hundreds of rows and set a value for each. But that job has to complete before I allow the users to do the next step and submit the updated rows to the database.

The javascript is below.

// toolbar events/actions 
changeZeroDiscountButton.click(function (event) {
    var value = discountComboBox.jqxComboBox('val');

    if ((value != null) && (value != "")) {
        value = value / 100;

        // get all the rows (this may have to be changed if we have paging 
        var datainformations = $('#jqxgrid').jqxGrid('getdatainformation');
        var rowscounts = datainformations.rowscount;

        for (var i = 0; i < rowscounts; i++) {
            var preSetDiscount = $("#jqxgrid").jqxGrid('getcellvalue', i, "discount");

            if (preSetDiscount == .0000) {
                $("#jqxgrid").jqxGrid('setcellvalue', i, "discount", value);
            }
        }
    }

});
Community
  • 1
  • 1
NJA
  • 57
  • 3
  • 10

2 Answers2

1

JavaScript is designed so it does not block the UI in any way, and this is one of its most important features for the browsers. The only exceptions are the popup message boxes (i.e. alert(), confirm(), and propmpt()). Even if it is possible, it's highly not recommended to block the UI.

There are many alternative ways to prevent the user from firing actions that shouldn't be fired until something else happens. Examples:

  • Disable the action's button until your processing ends then enable it back.
  • Set a flag (e.g. var processing = true) and check that flag in the click event of the action's button so it displays a message (e.g. "still processing, please wait...") when flag is true and execute the action when flag is false. Remember not to use alert() for the message otherwise you'll block the processing. Use a popup div instead.
  • Set the event handler at the beginning of the processing to a function that displays a message (e.g. "still processing, please wait...") and at the end of the processing, set the event handler to the function that will do the action. Remember not to use alert() for the message otherwise you'll block the processing. Use a popup div instead.
  • Show a modal popup div at the beginning of the processing with a message (e.g. "still processing, please wait..."), or progress bar, or some animation. The modal popup prevents the user from interacting with the page so they cannot click anything. For that to work, the modal popup must not have a close button or any other way to close it. At the end of processing, close the modal popup so the user can now continue.

Important Note: You mentioned in your comment to the other answer that the overlay (which is similar to the modal popup in my last point) is not displayed until the end of processing. That's because your processing is occupying the processor and preventing it from handling the UI thread. When you can do is delay your processing. So first display the modal popup (or overlay), then use setTimeout() to start processing 1 second later (maybe 500 millisecond or even less is enough). This gives the processor enough time to handle the UI thread before it starts your long processing.

Edit Here is an example of the last method:

function start() {
  disableUI();
  setTimeout(function() {
    process();
  }, 1000);
}

function process() {
  var s = (new Date()).getTime();
  var x = {};
  for (var i = 0; i < 99999; i++) {
    x["x" + i] = i * i + i;
  }
  var e = new Date().getTime();
  $("#results").text("Execution time: " + (e - s));
  enableUI();
}

function disableUI() {
  $("#uiOverlay").dialog({
    modal: true,
    closeOnEscape: false,
    dialogClass: "dialog-no-close",
  });
}

function enableUI() {
  $("#uiOverlay").dialog("close");
}
.dialog-no-close .ui-dialog-titlebar {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

<button type="button" onclick="start()">Start</button>
<div id="results"></div>
<div id="uiOverlay" style="display: none;">Processing... Please wait...</div>

Edit 2 Here is an example of the third method:

$("#StartButton").on("click", start);

function start() {
  //remove all previous click events
  $("#StartButton").off("click");
  //set the click event to show the message
  $("#StartButton").on("click", showProcessingMsg);
  //clear the previous results
  $("#results").text("");
  setTimeout(function() {
    process();
  }, 1000);
}

function process() {
  var s = (new Date()).getTime();
  var x = {};
  for (var i = 0; i < 99999; i++) {
    x["x" + i] = i * i + i;
  }
  var e = new Date().getTime();
  $("#results").text("Execution time: " + (e - s));

  //remove all previous click events
  $("#StartButton").off("click");
  //set the click event back to original
  $("#StartButton").on("click", start);
}

function showProcessingMsg() {
  $("#results").text("Still processing, please wait...");
}
.dialog-no-close .ui-dialog-titlebar {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button type="button" id="StartButton">Start</button>
<div id="results"></div>
Racil Hilan
  • 24,690
  • 13
  • 50
  • 55
  • Racil, do you have an example of the third solution you can point me at? While the last solution seems the easiest, no matter what I do I can not get the div to show before the processing starts. – NJA Jul 31 '16 at 19:22
  • I've added an example for you on how to do it in the forth method (the UI overlay). If you still wan an example of the third method, let me know. – Racil Hilan Aug 01 '16 at 02:19
  • An example of the third method would be greatly appreciated. I can't actually get the overlay method to work, so I am using SweetAlert2, which as you point out is an alert and is specifically designed to block the UI. – NJA Aug 02 '16 at 14:32
  • I've added an example of the third method. – Racil Hilan Aug 03 '16 at 19:57
0

If it is a long loop browser by itself will block all other events. You may just experience a freeze browser.

That wont be a good experience.

You can look cover the UI with an Overlay like this and inform user about the operation

brk
  • 48,835
  • 10
  • 56
  • 78
  • I added the OpenNav() funcion to the beginning of my code but the overlay doesn't show until *after* the orginal javascript finishes.
    `changeZeroDiscountButton.click(function (event) { openNav(); var value = discountComboBox.jqxComboBox('val'); if ((value != null) && (value != "")) { `
    – NJA Jul 30 '16 at 17:19