众所周知,JavaScript在所有现代浏览器实现中都是单线程的,但这是在任何标准中指定的还是只是传统?假设JavaScript总是单线程的,这是完全安全的吗?


当前回答

我想说的是,该规范并没有阻止人们创建一个在多线程上运行javascript的引擎,要求代码执行同步以访问共享对象状态。

我认为单线程非阻塞模式是出于在浏览器中运行javascript的需要,ui不应该阻塞。

Nodejs遵循了浏览器的方法。

然而,Rhino引擎支持在不同的线程中运行js代码。执行不能共享上下文,但可以共享作用域。 对于这个特定的案例,文档说明:

...Rhino保证了对JavaScript对象属性的访问是跨线程的原子性的,但不能保证脚本在同一作用域内同时执行。如果两个脚本同时使用相同的作用域,则脚本负责协调对共享变量的任何访问。”

通过阅读Rhino文档,我得出结论,对于某些人来说,编写一个javascript api也可以生成新的javascript线程,但api将是特定于Rhino的(例如,节点只能生成一个新进程)。

我想,即使是支持多线程的javascript引擎,也应该兼容不考虑多线程或阻塞的脚本。

我认为浏览器和nodejs之间的关系是这样的:

所有的js代码都在一个线程中执行吗?:是的。 js代码可以导致其他线程运行吗?:是的。 这些线程会改变js的执行上下文吗?:没有。但是它们可以(直接/间接地(?))追加到事件队列中 侦听器可以改变执行上下文。但是不要被愚弄,侦听器再次在主线程上原子地运行。

所以,对于浏览器和nodejs(可能还有很多其他引擎)来说,javascript不是多线程的,但引擎本身是多线程的。


关于网络工作者的更新:

网络工作者的存在进一步证明了javascript可以是多线程的,从某种意义上说,有人可以用javascript创建代码,这些代码将在单独的线程上运行。

然而,web工作者并不能解决那些可以共享执行上下文的传统线程的问题。上面的规则2和3仍然适用,但这次线程代码是由用户(js代码作者)在javascript中创建的。

从效率(而不是并发性)的角度来看,唯一需要考虑的是衍生线程的数量。见下文:

关于线程安全:

The Worker interface spawns real OS-level threads, and mindful programmers may be concerned that concurrency can cause “interesting” effects in your code if you aren't careful. However, since web workers have carefully controlled communication points with other threads, it's actually very hard to cause concurrency problems. There's no access to non-threadsafe components or the DOM. And you have to pass specific data in and out of a thread through serialized objects. So you have to work really hard to cause problems in your code.


P.S.

除了理论之外,对可能出现的极端情况和公认答案中描述的错误要时刻做好准备

其他回答

Chrome是多进程的,我认为每个进程都有自己的Javascript代码,但就代码所知,它是“单线程”的。

Javascript中不支持多线程,至少不是显式的,所以没有什么区别。

实际上,父窗口可以与拥有自己执行线程的子窗口或兄弟窗口或框架通信。

这是个好问题。我想说“是的”。我不能。

JavaScript通常被认为有一个对脚本可见的执行线程(*),因此当您的内联脚本、事件侦听器或超时输入时,您仍然完全处于控制状态,直到从块或函数的末尾返回。

(*:忽略了浏览器是否真的使用一个操作系统线程实现他们的JS引擎,或者WebWorkers是否引入了其他有限的执行线程。)

然而,在现实中,这并不完全正确。

最常见的情况是即时事件。当你的代码做了一些导致它们的事情时,浏览器会立即触发它们:

var l= document.getElementById('log'); var i= document.getElementById('inp'); i.onblur= function() { l.value+= 'blur\n'; }; setTimeout(function() { l.value+= 'login in\n'; l.focus(); l.value+= '注销\n'; }, 100); i.focus(); <textarea id=“log” rows=“20” cols=“40”></textarea> <input id=“inp”>

结果登录,模糊,注销除了IE。这些事件并不仅仅因为您直接调用了focus()而触发,它们也可能因为您调用了alert(),或者打开了一个弹出窗口,或者其他任何移动焦点的操作而发生。

这也可能导致其他事件。例如,添加一个i.onchange监听器,在focus()调用取消聚焦之前在输入中输入一些东西,日志顺序是登录、更改、模糊、注销,除了在Opera中是登录、模糊、注销、更改,在IE中是(更难以解释的)登录、更改、注销、模糊。

类似地,在所有浏览器中,对提供click的元素调用click()会立即调用onclick处理程序(至少这是一致的!)

(我用的是…事件处理程序属性,但同样发生在addEventListener和attachEvent。)

在许多情况下,尽管您没有做任何事情来触发事件,但当您的代码被线程插入时,事件可能会被触发。一个例子:

var l= document.getElementById('log'); document.getElementById('act').onclick= function() { l.value+= 'alert in\n'; 警报(“警报! l.value+= 'alert out\n'; }; window.onresize= function() { l.value+= 'resize\n'; }; <textarea id=“log” rows=“20” cols=“40”></textarea> <按钮 id=“行动”>警报</button>

点击alert,你会得到一个模态对话框。在你终止对话之前不会再执行脚本,对吗?没有。调整主窗口的大小,你会在文本区得到警报进入,调整大小,警报退出。

你可能认为,当一个模态对话框打开时,调整窗口大小是不可能的,但事实并非如此:在Linux中,你可以随心所欲地调整窗口大小;在Windows上,这并不容易,但你可以通过将屏幕分辨率从较大的窗口调整为较小的窗口来实现,从而调整窗口的大小。

You might think, well, it's only resize (and probably a few more like scroll) that can fire when the user doesn't have active interaction with the browser because script is threaded. And for single windows you might be right. But that all goes to pot as soon as you're doing cross-window scripting. For all browsers other than Safari, which blocks all windows/tabs/frames when any one of them is busy, you can interact with a document from the code of another document, running in a separate thread of execution and causing any related event handlers to fire.

在脚本仍然线程化的情况下,你可以引起事件生成的地方:

when the modal popups (alert, confirm, prompt) are open, in all browsers but Opera; during showModalDialog on browsers that support it; the “A script on this page may be busy...” dialogue box, even if you choose to let the script continue to run, allows events like resize and blur to fire and be handled even whilst the script is in the middle of a busy-loop, except in Opera. a while ago for me, in IE with the Sun Java Plugin, calling any method on an applet could allow events to fire and script to be re-entered. This was always a timing-sensitive bug, and it's possible Sun have fixed it since (I certainly hope so). probably more. It's been a while since I tested this and browsers have gained complexity since.

总之,对于大多数用户来说,JavaScript在大多数情况下都具有严格的事件驱动的单线程执行。在现实中,它没有这样的东西。目前还不清楚其中有多少是简单的错误,有多少是故意设计的,但如果您正在编写复杂的应用程序,特别是跨窗口/框架脚本的应用程序,那么它很可能会以间歇性的、难以调试的方式咬你一口。

如果出现最糟糕的情况,您可以通过间接引导所有事件响应来解决并发问题。当事件传入时,将其放入队列中,然后在setInterval函数中按顺序处理队列。如果您正在编写一个用于复杂应用程序的框架,那么这样做可能是一个很好的举措。postMessage还有望在将来缓解跨文档脚本编写的痛苦。

我的答案是肯定的——因为如果浏览器的javascript引擎异步运行,几乎所有现有的(至少所有非平凡的)javascript代码都会崩溃。

再加上HTML5已经指定了Web Workers(用于多线程javascript代码的显式标准化API),在基本javascript中引入多线程是毫无意义的。

(请其他评论者注意:即使setTimeout/setInterval, http请求onload事件(XHR),和UI事件(点击,聚焦等)提供了一个多线程的粗略印象-他们仍然都是沿着单一的时间轴执行-一次一个-所以即使我们事先不知道他们的执行顺序,也没有必要担心外部条件在事件处理程序,定时函数或XHR回调的执行过程中发生变化。)

@Bobince提供了一个非常模糊的答案。

引用Már Örlygsson的回答,Javascript总是单线程的,因为这个简单的事实:Javascript中的所有内容都是沿着单一的时间轴执行的。

这是单线程编程语言的严格定义。