众所周知,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.
除了理论之外,对可能出现的极端情况和公认答案中描述的错误要时刻做好准备
其他回答
尝试在彼此内嵌套两个setTimeout函数,它们将表现为多线程(即;外部计时器在执行其功能之前不会等待内部计时器完成)。
No.
我要跟大家唱反调,但请大家耐心听我说。单个JS脚本的目的是有效的单线程,但这并不意味着它不能被不同的解释。
假设您有以下代码……
var list = [];
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
这是在期望到循环结束时,列表必须有10000个索引平方的条目,但VM可能会注意到循环的每次迭代都不影响其他迭代,并使用两个线程重新解释。
第一个线程
for (var i = 0; i < 5000; i++) {
list[i] = i * i;
}
第二个线程
for (var i = 5000; i < 10000; i++) {
list[i] = i * i;
}
这里我简化了,因为JS数组比内存块更复杂,但如果这两个脚本能够以线程安全的方式向数组中添加条目,那么当它们都完成执行时,它将得到与单线程版本相同的结果。
虽然我不知道有哪个VM可以检测这样的可并行代码,但它似乎在将来会出现在JIT VM中,因为它在某些情况下可以提供更快的速度。
进一步应用这个概念,可以对代码进行注释,让VM知道要将哪些代码转换为多线程代码。
// like "use strict" this enables certain features on compatible VMs.
"use parallel";
var list = [];
// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
自从Web worker开始使用Javascript,不太可能……更丑陋的系统将会出现,但我认为可以肯定地说Javascript传统上是单线程的。
是的,尽管Internet Explorer 9会在一个单独的线程上编译你的Javascript,为在主线程上执行做准备。但是,对于作为程序员的您来说,这并没有任何改变。
是的,尽管在使用任何异步api(如setInterval和xmlhttp回调)时仍然会遇到并发编程的一些问题(主要是竞争条件)。
这是个好问题。我想说“是的”。我不能。
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 toISOString()忽略时区偏移
- 如何在JavaScript中获得当前时间
- 我如何从一个元标签与JavaScript的信息?
- 匹配精确字符串
- Haskell对Node.js的响应是什么?
- 用JavaScript创建一个基于字符串的十六进制颜色
- JavaScript数据网格数百万行
- 我可以在JavaScript中获得当前运行函数的名称吗?
- 如何防止输入键提交网页表单?
- 在html文本框中设置键盘插入符号的位置
- 使用jQuery选择多个类
- Cypress:只运行一个测试
- 如何同步确定JavaScript Promise的状态?
- 在Link react-router中传递道具
- 我如何承诺本地XHR?