如果之后调用它,为什么这个用于循环阻塞?

时间:2014-01-29 07:05:22

标签: javascript

为什么在for循环结束之前div[id=box]不会更新?如果我注释掉for循环,div会立即显示。

document.getElementById('click').onclick = function() {
    document.getElementById('box').style.display = 'block';

    // loop after element update
    for (var i = 0; i < 2000000000; ++i) {}
};

http://jsfiddle.net/472BU/

4 个答案:

答案 0 :(得分:2)

简单地说,所有浏览器进程(JS,重新绘制页面,甚至响应用户点击/按键,并且在大多数情况下刷新页面更改......甚至关闭选项卡)都发生在同一个进程线程中。 / p>

值得庆幸的是,这不是100%真实,100%的时间,不再。 某些浏览器供应商正在努力将网络平台的不同部分移动到不同的线程,以获得更流畅的体验,但通常情况下,如果您锁定了JS,则锁定所有内容。

这只是意味着浏览器在JS完成运行之前不会实际重绘,并将控制权交还给DOM。

好消息是,这意味着您可以在功能结束时通过取消隐藏它们,抓取它们的尺寸并再次隐藏它们来测量元素。它们占用的宽度/高度是在现场计算的,但如果更改元素,则可能必须绘制页面的大部分,因此如果可以在循环中更改30000个元素,则将它们全部绘制为发生这将是一件非常糟糕的事情。

答案 1 :(得分:2)

其他原因已经解释了原因。如果您想立即绘制盒子,解决方案很简单。将循环置于超时状态:

document.getElementById('click').onclick = function() {
  document.getElementById('box').style.display = 'block';
  // no delay anymore
  setTimeout( function(){for (var i = 0; i < 2000000000; ++i) {}},10);
};

jsFiddle

同时检查web workers

答案 2 :(得分:1)

持续运行的迭代次数将耗尽所有浏览器的资源,并且无法担心应用样式。

您的javascript按其出现的顺序执行,但在幕后有一个用于呈现样式更改的队列。在任何正常使用中,您都不会注意到这种行为,但由于您运行的是性能较差的循环,因此很明显。

答案 3 :(得分:1)

问题

这是因为JavaScript是单线程的,只能运行该循环。

只要循环持续,其他任何东西都将被暂停。由于DOM连接到JavaScript,DOM也会被阻塞(通常,除了在单独的线程上运行DOM的浏览器中,并且将为事件队列生成事件,而事件队列将被保留,直到当前执行范围具有完成)。

解决方案

为避免这种情况,您需要将函数拆分为多个异步操作(与多线程不同),这将使浏览器能够调用在事件队列中排队的某些事件(例如绘制事件)。 / p>

您可以通过拆分函数来执行此操作,以使用内部机制来分段执行迭代。

例如:

Live demo

function busyLoop(callback) {

    var segCounter = 0,    /// keep track of segment
        totCounter = 0,    /// keep track of total count
        max = 2000000000,  /// max count
        segment = 1000000; /// segment size (smaller = better response)

    /// invoke first batch
    (function nextBatch() {

        segCounter = 0;    /// reset segment counter for each time

        for(; segCounter < segment && totCounter <= max; segCounter++, totCounter++) {
           ///...work here...
        }

        if (totCounter < max) {
            /// call setTimeout() which makes it async, +/- 11ms gives browser
            /// chance to process other events such as paint events:
            setTimeout(nextBatch, 11);

            /// optional progress callback here
        } else
            callback();
    })();
}

然后用回调函数调用它:

busyLoop(doneFunction);

请注意,您现在可以与DOM进行交互并获得反馈。

提示:较小的片段对DOM的响应速度越快,但总延迟时间越长,累积时间越长。尝试找到适合您解决方案的天平。

希望这有帮助。