用户界面-如何为长时间运行的Javascript循环阻塞UI

user interface - How to block the UI for a long running Javascript loop

本文关键字:循环 Javascript UI 运行 长时间 用户界面      更新时间:2023-09-26

我需要做与这篇文章相反的事情,"在不阻塞UI的情况下迭代数组的最佳方法"

我必须循环浏览数百行,并为每一行设置一个值。但是,在我允许用户执行下一步并将更新的行提交到数据库之前,必须完成该工作。

javascript如下。

// 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);
            }
        }
    }
});

JavaScript的设计使其不会以任何方式阻塞UI,这是其对浏览器最重要的功能之一。唯一的例外是弹出消息框(即alert()confirm()propmpt())。即使可能,也强烈建议不要阻止用户界面。

有许多其他方法可以防止用户启动在发生其他事情之前不应该启动的操作。示例:

  • 禁用操作的按钮,直到处理结束,然后重新启用
  • 设置一个标志(例如var processing = true),并在操作按钮的点击事件中检查该标志,使其在标志为true时显示一条消息(例如"仍在处理,请等待…"),在标志为false时执行操作。记住不要对消息使用alert(),否则会阻止处理。请使用弹出的div
  • 将处理开始时的事件处理程序设置为显示消息的函数(例如"仍在处理,请稍候…"),并在处理结束时将事件处理程序设为将执行操作的函数。记住不要对消息使用alert(),否则会阻止处理。请使用弹出的div
  • 在处理开始时显示模式弹出div,并显示消息(例如"仍在处理,请稍候…")、进度条或一些动画。模式弹出阻止用户与页面交互,因此他们无法单击任何内容。要做到这一点,模式弹出窗口必须没有关闭按钮或任何其他方式来关闭它。在处理结束时,关闭模式弹出窗口,以便用户现在可以继续

重要提示:您在对另一个答案的评论中提到,覆盖(类似于我最后一点中的模式弹出)直到处理结束才会显示。这是因为您的处理占用了处理器,并阻止它处理UI线程。你能做的就是延迟处理。因此,首先显示模式弹出(或覆盖),然后使用setTimeout()在1秒后开始处理(可能500毫秒甚至更短就足够了)。这给了处理器足够的时间来处理UI线程,然后再开始长时间的处理。

编辑下面是最后一种方法的示例:

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>

编辑2下面是第三种方法的示例:

$("#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>

如果它是一个长循环浏览器,它本身将阻止所有其他事件。您可能只是体验到浏览器冻结。

那不是一次好的经历。

您可以像这样用Overlay覆盖UI,并通知用户操作