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


当前回答

尝试在彼此内嵌套两个setTimeout函数,它们将表现为多线程(即;外部计时器在执行其功能之前不会等待内部计时器完成)。

其他回答

JavaScript/ECMAScript被设计为存在于宿主环境中。也就是说,除非宿主环境决定解析和执行给定的脚本,并提供让JavaScript真正有用的环境对象(例如浏览器中的DOM),否则JavaScript实际上什么也不做。

我认为给定的函数或脚本块将逐行执行,这是JavaScript的保证。然而,宿主环境可能同时执行多个脚本。或者,宿主环境可以始终提供一个提供多线程的对象。setTimeout和setInterval是一些例子,或者至少是伪例子,它们说明一个主机环境提供了一种实现并发性的方法(即使它不是真正的并发性)。

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

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

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

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

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

尝试在彼此内嵌套两个setTimeout函数,它们将表现为多线程(即;外部计时器在执行其功能之前不会等待内部计时器完成)。

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

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还有望在将来缓解跨文档脚本编写的痛苦。