300,000,000次迭代循环表现得非常奇怪

时间:2013-08-20 16:05:38

标签: javascript loops execution-time

在尝试回答this question时,我遇到了一种奇怪的行为(这是不一样的:他的迭代次数太少,我的太多了):

HTML:

<button id="go">it will be legend...</button>
<div id="output"></div>

JS:

var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
    output.textContent += 'wait for it...';
    for (var i=0; i<3000000000; i++) {
        var unused = i; // don't really care
    }
    output.textContent += ' dary!';
};

循环需要几秒钟才能执行,因为它有3,000,000,000次迭代。

点击按钮后,我的期望是:

  1. wait for it...出现
  2. 由于循环
  3. ,该过程会冻结一点
  4. dary!出现
  5. 实际发生了什么:

    1. 由于循环
    2. ,该过程会冻结一点
    3. wait for it... dary! 一起出现
    4. 知道为什么会出现这种行为吗?

      自己检查:fiddle

5 个答案:

答案 0 :(得分:17)

原因是函数整体上是同步执行的。当您将输出设置为wait for it...时,它将进入长时间运行的循环并占用线程。如果将其余部分包裹在timeout中,则第一个文本将显示为正常。

var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
    output.textContent += 'wait for it...';
    window.setTimeout(function() {
    for (var i=0; i<3000000000; i++) {
        var unused = i; // don't really care
    }
    output.textContent += ' dary!';
    }, 0);
};

请注意,处理时仍会冻结用户界面。

修改:使用0作为Chrome中的延迟值,但最新的Firefox和IE 10中没有。在两种情况下都将值更改为10

答案 1 :(得分:10)

Javascript几乎是单线程的。如果您正在运行代码,则该页面无响应,并且在您的代码完成之前不会更新。 (请注意,这是特定于实现的,但这就是今天所有浏览器都这样做的。)

答案 2 :(得分:2)

黑暗猎鹰和西蒙·贝朗格为这个事业提供了解释;这篇文章讨论了另一种解决方案但是,这个解决方案绝对不适合30亿次迭代循环,因为它比较慢。

根据this SO post by user Cocco,为此目的,setTimeout使用requestAnimationFrame的效果不如$(document).ready(function() { var W = window, D = W.document, i = 0, x = 0, output = D.getElementById('output'); function b() { if (x == 0) { output.textContent = 'wait for it...'; x++; } i++; if (i < 300) { //if (i > 20) output.textContent = i; requestAnimationFrame(b); } else { D.body.style.cursor = 'default'; output.textContent += ' dary!'; } } function start() { console.log(D) D.body.style.cursor = 'wait'; b(); } D.getElementById('go').onclick = start; }); //END $(document).ready() 。那么,这是如何使用requestAnimationFrame:

jsFiddle example

{{1}}

注意:为了表示同意/赞赏,请在上面的帖子中提升Cocco's answer而不是这个。谢谢。

答案 3 :(得分:1)

您的代码正在按预期执行。问题是浏览器不会在javascript完成之后显示对文档的更改。超时是通过将代码执行分解为两个单独的事件来解决此问题。以下代码将显示您的预期正在发生。

var output = document.getElementById('output');
document.getElementById('go').onclick = function() {
    console.log('wait for it...';)
    for (var i=0; i<3000000000; i++) {
        var unused = i; // don't really care
    }
    console.log(' dary!');
};

使用超时解决方案时也需要小心,因为执行不再是同步的。

output = document.getElementById('output');
document.getElementById('go').onclick = function() {
    output.textContent += 'wait for it...';
    window.setTimeout(function() {
        for (var i = 0; i < 3000000000; i++) {
        var unused = i;
        // don't really care
        }
    output.textContent += ' dary!';
    }, 0);
    output.textContent += ' epic';
};

如果您运行此版本,您会注意到'epic'在'dary'之前。

答案 4 :(得分:0)

我看到的唯一解释是浏览器在执行javascript后刷新视图?作为证据,这可以按预期工作:

var output = document.getElementById('output');
document.getElementById('go').onclick = function () {
    output.textContent += 'wait for it...';
    window.setTimeout(count, 100);
};

function count() {
    for (var i = 0; i < 3000000000; i++) {
        var unused = i; // don't really care
    }
    output.textContent += ' dary!';
}