为什么JavaScript的SetTimeout()不是多线程的

12

我有一个测试:

HTML:

<div id="f1">Empty</div>
<div id="f2">Empty</div>

​

JavaScript:

var s1 = function() {
    for (i = 1; i < 1000000000; i++) {
        var b = i * i;
    }
    $('#f1').html('Set');
}

var s2 = function() {
    if ($('#f1').html() == 'Empty') {
        $('#f2').html('Multi Thread');
        return;            
    };
    $('#f2').html('One Thread');
}

setTimeout(s2,110);
setTimeout(s1,100);​

为什么setTimeout()不能像事件模型一样在不同的线程中运行?

jsfiddle


5
因为JavaScript不支持多线程? - PeeHaa
@PeeHaa 但为什么不呢?它至少有异步函数... - zb'
3
异步函数不需要线程,只需要能够排队事件的能力,以及存在一个事件循环,从队列中取出事件并执行它们。 - Alnitak
8个回答

17

Javascript 本身并不是多线程或非多线程。然而,目前在主流浏览器中实现的 Javascript 大都是单线程的。

此外,要进行正确的多线程编程,语言需要具备共享内存、锁、信号量和其他并发编程工具,然而当前定义的 JavaScript 并没有这些工具(例如,无法描述并发JS线程如何控制谁可以更新DOM对象,因为在窗口中只有一个DOM对象是共享的)。

有一些尝试让JS更加并行化——比如看看 web workers、Intel 的 River Trail、Google 的 HTML5更多


for (i = 1; i < 1000000000; i++) { 放在一个 worker 中,并让它的 resultReceiver 执行 $('#f1').html('Set'); - Paul S.

4

eicto, setTimeout函数并不会在请求时立即执行代码。
它将代码排队,与它之前的所有代码一起内联,并将其位置设置为最小等待时间。

此外,大多数浏览器都有最小超时限制。
如果您请求1毫秒的超时,那么在大多数浏览器中,您将在10毫秒至15毫秒后收到响应。

所有JS与DOM的交互以及实际上单个页面所做的几乎所有事情都在一个线程上完成,除了自定义浏览器扩展和一些新API(如Web Workers)之外。

这就是为什么大型项目需要考虑页面中的其他所有内容以及为什么所有内容都需要异步处理的原因。

由于setTimeout不是sleep,并且不会在被计划的微秒精确返回...它会将回调放置在事件堆栈中,在不早于您指定的时间时执行。


即使在Windows API中也不能设置1毫秒的时间。C++中的Sleep(1)将休眠15至30毫秒。 WaitForSingleObject()同样适用。这是由于Windows的内部设计不是实时操作系统所致。 - Elmue

4
很多设计决策都考虑了在浏览器中实现JavaScript时假定只有单线程访问浏览器DOM和其他全局变量/属性。这使得使用它进行编程更不容易出问题,但也引入了一些必须处理的限制。
语言本身完全能够支持多线程,我们已经在WebWorkers和某些服务器实现的语言中看到了这一点。但是,任何时候使用多个线程并尝试读取/写入在多个线程之间共享的变量或属性时,必须使用保护设备(如互斥锁)以允许可靠地访问这些共享资源。这显着增加了如何进行此编程的复杂性,而在浏览器中的JavaScript则决定不要求掌握这种水平以可靠地编程。
对于任何已经进行过多线程编程的人来说,它可能很强大,但很容易引入难以找到的错误。负责浏览器中JavaScript的人决定应该完全避免这种难度水平和由此产生的错误类型。
即使现在使用WebWorkers,WebWorker和主JavaScript线程之间也没有共享资源。两者必须通过消息传递系统进行通信,这是一种绝对安全的方式。因此,不能从WebWorker访问DOM。相反,如果要更改DOM,则必须向单个主线程发送消息并要求它更新DOM。当主线程完成其他任务后才会接收到该消息(它是单线程的)。
还有可能DOM现在已经花费了无数年作为仅设计用于单线程访问的结构,因此设计和实现从多个线程访问它的方法并修复该实现中的所有错误将是一个巨大的任务。

2

Javascript不支持多线程,因为浏览器中的解释器是单线程的。


好的,但为什么不至少中断先前的函数以及时运行代码呢? - zb'
3
一个非常聪明的回答!它不是多线程的,因为它是单线程的! - Elmue
每个基于UI的程序都始于一个单线程,也称为UI线程,然后您可以创建更多的线程。这与“浏览器中的解释器是单线程”无关。 - Teoman shipahi

1

JavaScript不支持多线程,即使它支持setTimeout也是同步的。setTimeout和setInterval由浏览器提供,属于JavaScript语言之外的外部访问方式,例如事件执行。当人们将JavaScript称为异步或多线程语言时,他们可能指的是这一点,因为多个外部访问点(如多个计时器或事件执行)可以同时发生,每个访问点都会在内存中生成一个独特的解释器访问点。这正是Node.js开发人员在声称JavaScript时所指的内容。

这种多个外部访问各个隔离线程的方式可能会导致UI冲突,因为模拟多线程效果很可能会导致浏览器输出冲突,而页面上只有一个表示整个页面的文档对象。这就是为什么通常认为使用短间隔的setInterval是不安全的原因。setInterval完全是异步的,即使前一个间隔的执行尚未结束,它也会按照提供的间隔执行。这种冲突是我所说的“故障”,因为下一个间隔正在执行覆盖前一个执行的代码,如果您的代码需要访问DOM或使用闭包,您可能会遇到问题。为了安全起见,建议使用递归setTimeout,因为它是同步的,下一轮执行将在前一轮完成后才会发生。


这里我们看到一个例子,当SetTimeout是同步的时候,其他超时等待第一个执行。 - zb'

0

Mozilla在Javascript中支持多线程,只要您不想从多个线程执行UI工作。我删除重复消息(备用)扩展的早期版本是多线程的。

有关此问题的详细信息,请参阅我的自己的错误页面,或更好地,参阅此页面了解如何在Mozilla中使用工作线程。eicto,您可以使用后台线程很好地实现您的代码。


0

Javascript 不支持多线程。

HTML5 将赋予 Javascript 多线程能力。


-1

在等待给定的时间后,方法(s1,s2)的执行仍然发生在JavaScript线程本身中(即单线程)。

s2等待s1的原因。


1
为什么会有负评?请解释一下,如果有什么问题的话,这可以帮助我学习。;) - Anand A.S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接