6

我一直在阅读 John Resig 的“JavaScript Ninja 的秘密”,它解释了 JavaScript 是单线程的。但是,我尝试对此进行测试,但不确定要从这里拿走什么:

// executing this in browser
(function () {
    // throw something into event queue
    setTimeout(function () {
        alert("This will be called back after 1 second.");
    }, 1000);

    // arbitrary loop to take up some time
    for (var i = 0; i < 10000; i += 1) {
        console.log(i);
    }
})();

也许我不完全理解单线程意味着什么,但我认为 setTimeout 回调在所有外部匿名函数完成之前不会执行。但是,在浏览器中运行它表明回调函数在 i 仍在输出到控制台时被调用。对我来说,这似乎有 2 个线程调用匿名函数占用 1 个线程,然后使用第 2 个线程进行回调。

有人可以帮我解惑吗?

4

3 回答 3

11

console.log()在某些浏览器(如 Chrome)中是一个奇怪的功能,并且本身不是同步的,因此您不能真正使用它来衡量单线程。您可能看到的是 JS 引擎执行所有console.log()语句,然后setTimeout()运行以显示警报,并且并行(在其他一些不是 javascript 的进程中)所有数据都显示在控制台中。

Javascript 确实是单线程的。在您的示例中,直到您的循环完成,setTimeout()回调才会执行。for

您可以像这样更好地说明它:

(function () {
    // throw something into event queue
    setTimeout(function () {
        alert("This will be called back after 1 second.");
    }, 1000);

    function now() {
        return new Date().getTime();
    }
    var start = now();
    // loop for 1.2 seconds
    while (now() - start < 1200) {}
    alert("Looping done");
})();

工作 jsFiddle 演示:http: //jsfiddle.net/jfriend00/3sBTb/

于 2014-01-29T22:05:56.913 回答
4

这是一个有点难以理解的概念。加入事件监听器之类的东西也会进一步混淆画面。

一种简单的思考方式就像你有一条传送带。你的正常函数调用都是均匀分布的,中间有空间。

异步事物(超时、触发事件等)填补了这些地方。每个正常调用之间没有无限量的空间,所以它可以从这个队列中得到它,做更多的正常同步函数,用异步填充更多的空间,等等。

这种影响似乎是多线程的(实际上您可以通过异步调用导致某种竞争条件)。在许多情况下,区别并不重要。但是,记住这一点很重要。

但是,当您尝试调试事物时,尤其是在使用 Chrome 之类的工具时console.log,它可能看起来像是被打乱了,因为 console.log 本身是异步的(如果它是同步的,它会将您的脚本冻结在一个长函数上)。

如果您输出如下内容,您可以自己看到:

var input = document.createElement('input');
input.setAttribute('value', 0);

function inc() {
    input.setAttribute('value', parseInt(input.getAttribute('value'))+1);
    console.log(input);

    if (parseInt(input.getAttribute('value')) < 100) {
        setTimeout(inc, 10);
    }
}

inc();

JSFiddle:http: //jsfiddle.net/c2PnP/

该脚本的作用是创建一个输入元素,然后每 10 毫秒递增输入值,然后输出输入元素。它重复 100 次(直到值 = 100)。

如果您查看控制台,您会注意到某些值会重复,这不会是一个平稳的进程。例如,在我刚刚进行的一次运行中,我看到 5 个输入值为“100”,并且缺少数字的间隙。这是因为 console.log 是异步运行的,并且只有在有间隙时才输出。

(注意:如果您有一台超级快速的计算机,您可能需要将时间减少到更小的值,例如 1,和/或将迭代次数增加到更大的数字)。

于 2014-01-29T22:20:14.467 回答
3

John Resig 很好地涵盖了这一点。 总结:

“JavaScript 一次只能执行一段代码(由于其单线程性质)......当异步事件发生时(如鼠标单击、计时器触发或 XMLHttpRequest 完成),它会排队等待稍后执行.... JavaScript 的初始块执行完后,浏览器立即提出问题:等待执行的是什么?然后浏览器选择一个并立即执行。[rest] 将等待到下一个可能的时间,为了执行。”

于 2014-01-29T22:05:50.323 回答