14

在尝试回答这个问题时,我遇到了一个奇怪的行为(这是不一样的:他是由于迭代太少,我的迭代太多):

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. dary!出现

实际发生了什么:

  1. 由于循环,该过程有点冻结
  2. wait for it... dary!一起出现

知道为什么会有这种行为吗?

自己检查:小提琴

4

5 回答 5

17

原因是函数作为一个整体是同步执行的。当您将输出设置为 时wait for it...,它会进入长时间运行的循环并占用线程。如果将其余部分包装在 a 中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);
};

请注意,它在处理时仍会冻结 UI。

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

于 2013-08-20T16:10:18.273 回答
10

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

于 2013-08-20T16:08:50.633 回答
2

Dark Falcon 和 Simon Belanger 提供了原因解释;这篇文章讨论了一个不同的解决方案。但是,这个解决方案绝对不适合 30 亿次迭代循环,因为相比之下它太慢了。

根据用户 Cocco 的这篇 SO 帖子,使用setTimeout效果不requestAnimationFrame如此目的。所以,这里是如何使用requestAnimationFrame:

jsFiddle 示例

$(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()

注意:为了表示同意/赞赏,请在上面的帖子中点赞Cocco 的回答,而不是这个帖子。谢谢你。

于 2013-08-20T18:20:35.563 回答
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'之前。

于 2013-08-25T01:04:52.730 回答
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!';
}
于 2013-08-20T16:15:01.330 回答