3

我有一个网页,其中显示了一个包含大量数据的图表。该图表是第 3 方 Flash 组件。

许多数据在初始页面加载后被延迟加载并输入到 AJAX 中的图表中。我的问题是,当发生这种情况时,页面会冻结几次,直到所有数据都加载完毕。这是一个问题,特别是因为页面加载时,似乎一切都准备好了,但是当使用 AJAX 加载数据时,页面冻结了。一个很好的例子是我有一个 setInterval,它每 1 秒增加一个时钟。该时钟在 AJAX 加载过程中视觉上冻结。

如何避免这种情况?你会推荐不同的装载结构吗?

4

2 回答 2

2

您的问题实际上非常简单,尽管解决方案有点棘手。您遇到“冻结”的原因是 JavaScript 是单线程的。页面的绘制发生在与数据处理相同的线程上。因此,如果您有一大块数据需要很长时间来处理,您将体验到用户界面冻结。

考虑以下示例:

http://jsfiddle.net/VT4Rs/

注意:如果单击“停止”按钮时屏幕被冻结,您可能需要发送一些垃圾邮件。此外,如果您的计算机/浏览器不如我的出色,您可能需要调整“WEIGHT”变量以防止超长时间冻结,或者如果您的计算机/浏览器比我的更出色,则导致冻结发生。

无论如何,关键是您会注意到“时钟”在循环所有这些数字时会定期冻结。原因是时钟更新发生在与数据“处理”相同的线程上。这种方式setInterval()有效,它将每X毫秒将一个事件(以运行它传递的函数)添加到您的事件队列中。但是,为了防止在函数运行时间超过X毫秒的情况下出现一些严重问题,如果事件的先前迭代已经在队列中,它将跳过将新事件添加到事件队列中。

因此,如果您有一些随机处理占用了您的线程,那么您setInterval的 s 显然将无法正常工作。此外,由于浏览器界面在同一线程上更新,您也可能导致“冻结”。

我发现解决此问题的最佳方法是将数据处理分解为更易于管理的“块”,然后setTimeout(fn, 0)定期将这些块推送到事件队列中,同时允许一些浏览器绘画和其他 JavaScript 根据需要进行处理。所以看看这个更新的例子,看看有什么不同。我们仍在“处理”相同的数据,但我们将其分成更小的块:

http://jsfiddle.net/Sjk29/

诀窍在于决定制作块的大小。您会发现将块调整为太大会导致停顿和错过间隔。但是如果你把它做得太小,由于每个块的内置开销,处理数据需要很长时间。

好消息是您不限于指定的大小。在我的示例中,块大小由变量确定,您可能会注意到每个块都引用了该变量。这意味着您可以根据用户浏览器的实际性能调整该变量以调整块大小。你可能会做这样的事情:

var CHUNK_SCALE = 8;
var CHUNK_SIZE = function() { return Math.pow(10, CHUNK_SCALE); };

(function() {
    var lastTick = new Date();

    setInterval(function() {
        var now = new Date();
        if (now - lastTick > 1500) {
            CHUNK_SCALE--;
        }
        lastTick = now;
    }, 1000);
}());

(见:http: //jsfiddle.net/ewP96/

这样,如果块大小开始太大(以可能影响 UI 性能的方式),那么它将自动将其缩放回用户计算机可以处理的范围。

JavaScript 非常棒,是吗?

于 2012-09-06T13:45:05.033 回答
1

我建议您使用 html5 而不是 flash 进行绘图,这可能会解决您的冻结问题。你可以查一下:

  1. http://g.raphaeljs.com/
  2. http://www.rgraph.net/
  3. http://www.zingchart.com/
  4. http://d3js.org/
  5. http://processingjs.org/
于 2012-09-13T02:56:03.357 回答