JavaScript 页面的非阻塞更新

JavaScript non-blocking updates to page

我有以下代码 运行 是一个循环并在页面运行时更新页面。目前页面不会更新,直到整个循环完成 运行。

如您所见,我尝试添加一个绘制函数 drawValues,每 5000 次调用该函数以将当前值绘制到屏幕上。我的理解是,当 drawValues 更新时,页面应该更新,然后主循环将继续计算,直到另一个 5000 循环。

目前页面不会更新,直到整个循环 运行s,以某种方式忽略对 drawValues

的所有其他调用

完整代码段:

/*jslint browser: true*/
/*global $, jQuery, alert*/

$(document).ready(function() {
  'use strict';
  var namesAtStart = ["Sam", "John"],
    specialNum = 8,
    amountOfNames = namesAtStart.length,
    counter = [],
    renderCounter = 0,
    x,
    a,
    loopLength,
    number,
    id,
    preId = "content_",
    finalId;

  for (a = 0; a < amountOfNames; a += 1) {
    counter.push(0);
  }

  for (x = 1; x <= specialNum; x += 1) {

    // Start the counter array at zero
    for (a = 0; a < amountOfNames; a += 1) {
      counter[a] = 0;
    }


    loopLength = Math.pow(10, x);
    finalId = preId + loopLength.toString();

    $(".output-small").append('<span id="' + finalId + '"></span>');

    for (a = 0; a < loopLength; a += 1) {
      number = Math.floor((Math.random() * amountOfNames) + 1);
      counter[number - 1] += 1;
      renderCounter += 1;
      if (renderCounter == 5000) {
        drawValues(namesAtStart, counter, finalId, x, a);
      }
      if (a == loopLength - 1) {
        // This is where I am trying to make the code non blocking and async
        drawValues(namesAtStart, counter, finalId, x, a);
      }
    }
  }
});

// This is the part that I want to run when called and update page.
function drawValues(names, counter, finalId, id, currentCount) {
  'use strict';

  var a;
  $("#" + finalId).empty();


  $("#" + finalId).append("<h3>" + Math.pow(10, id).toLocaleString() + "</h1>");

  for (a = 0; a < names.length; a += 1) {
    $("#" + finalId).append(
      names[a] + ": " + counter[a].toLocaleString() + " (" + (counter[a] / currentCount * 100).toFixed(2) + "%)</br>"
    );
  }

  $("#" + finalId).append("Numerical Difference: " + Math.abs(counter[0] - counter[1]) + "</br>");

  $("#" + finalId).append(
    "Percentage Difference: " + Math.abs(
      (counter[0] / currentCount * 100) - (counter[1] / currentCount * 100)
    ).toFixed(6) + "%</br>"
  );

  $("#" + finalId).append("</br>");


}
body {} p,
h3 {
  padding: 0px;
  margin: 0px;
}
.container {} .output {} .output-small {
  margin: 20px;
  padding: 5px;
  border: 1px solid rgba(0, 0, 0, 1);
  width: 300px;
  border-radius: 10px;
}
#stats-listing {}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>

<head>
  <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<title>Roll The Dice</title>

<body>
  <div class="container">
    <div class="output" id="stats-listing">
      <div class="output-small"></div>
    </div>
  </div>
  <script src="https://code.jquery.com/jquery-2.1.3.min.js"></script>
  <script src="logic.js"></script>
</body>

</html>

浏览器中用于运行JavaScript的主UI线程是单线程的。因此,如果您的某个功能花费了大量时间,则浏览器不会更新显示。

为了让浏览器有机会更新显示,您需要通过让当前函数结束并为下一个更新块安排对另一个 运行 的定时回调来返回它,通过 setTimeout。您必须尝试使用​​您想要支持的浏览器来确定定时回调的延迟;有些浏览器对 0 很满意(尽快回调),其他浏览器想要更长的时间(50 - 50 毫秒 - 对于我知道的每个浏览器来说都足够了)。

这是一个简单的示例,它向页面添加 10 个框,生成,然后再添加 10 个,生成,等等,直到完成 1,000 个框:

(function() {
  var total = 0;
  addBoxes();
  function addBoxes() {
    var n;
    
    for (n = 0; n < 10 && total < 1000; ++n, ++total) {
      box = document.createElement('div');
      box.className = "box";
      document.body.appendChild(box);
    }
    if (total < 1000) {
      setTimeout(addBoxes, 10); // 10ms
    }
  }
})();
.box {
  display: inline-block;
  border: 1px solid green;
  margin: 2px;
  width: 20px;
  height: 20px;
}