Ego*_*hin 588 javascript concurrency
众所周知,JavaScript在所有现代浏览器实现中都是单线程的,但是它是在任何标准中指定的还是仅仅是传统的?假设JavaScript始终是单线程的,这是完全安全的吗?
bob*_*nce 568
这是个好问题.我想说"是".我不能.
JavaScript通常被认为具有脚本(*)可见的单个执行线程,因此当您输入内联脚本,事件侦听器或超时时,您将保持完全控制,直到从块或函数结束返回.
(*:忽略浏览器是否真的使用一个OS线程实现其JS引擎的问题,或WebWorkers是否引入了其他有限的执行线程.)
然而,实际上这并不是真的,以偷偷摸摸的恶劣方式.
最常见的情况是即时事件.当您的代码执行某些操作时,浏览器会立即触发它们:
var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
l.value+= 'blur\n';
};
setTimeout(function() {
l.value+= 'log in\n';
l.focus();
l.value+= 'log out\n';
}, 100);
i.focus();Run Code Online (Sandbox Code Playgroud)
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">Run Code Online (Sandbox Code Playgroud)
log in, blur, log out除了IE之外的所有结果.这些事件不仅仅因为你focus()直接调用而触发,它们可能因为你调用alert(),或者打开一个弹出窗口,或任何其他移动焦点而发生.
这也可能导致其他事件.例如i.onchange,在focus()调用取消它之前添加一个监听器并在输入中键入一些东西,并且日志顺序是log in, change, blur, log out,除了在Opera中它log in, blur, log out, change和它所在的IE(甚至不太明显)log in, change, log out, blur.
类似地,调用click()提供它的元素会onclick立即在所有浏览器中调用处理程序(至少这是一致的!).
(我在这里使用直接on...事件处理程序属性,但是addEventListener和attachEvent.相同.)
尽管你没有采取任何措施来激活事件,但是在你的代码被线程化的情况下,事件可能会发生一系列情况.一个例子:
var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
l.value+= 'alert in\n';
alert('alert!');
l.value+= 'alert out\n';
};
window.onresize= function() {
l.value+= 'resize\n';
};Run Code Online (Sandbox Code Playgroud)
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>Run Code Online (Sandbox Code Playgroud)
点击alert,你会得到一个模态对话框.在您解除对话之前不再执行脚本,是吗?不.调整主窗口的大小,您将进入alert in, resize, alert outtextarea.
您可能认为在模式对话框启动时调整窗口大小是不可能的,但不是这样:在Linux中,您可以根据需要调整窗口大小; 在Windows上它并不那么容易,但是你可以通过将屏幕分辨率从较大的屏幕分辨率更改为较小的屏幕分辨率(窗口不适合)来实现,从而使其调整大小.
您可能会认为,当用户没有与浏览器进行活动交互时,它resize可能scroll会触发(也可能更像一些),因为脚本是线程化的.对于单个窗口,您可能是对的.但是,只要你在进行跨窗口脚本编写,这一切都会进入底池.对于Safari以外的所有浏览器,当其中任何一个窗口/标签/框架忙时,它们会阻止所有窗口/标签/框架,您可以从另一个文档的代码中与文档交互,在单独的执行线程中运行并导致任何相关的事件处理程序火.
在脚本仍处于线程状态时,可以引发可以导致生成的事件的位置:
当模式弹出窗口(alert,confirm,prompt)是开放的,在所有的浏览器,但歌剧;
在showModalDialog支持它的浏览器上;
"此页面上的脚本可能正忙..."对话框,即使您选择让脚本继续运行,也可以调整调整大小和模糊等事件,即使脚本处于中间位置也可以处理繁忙的循环,除了Opera.
不久前,对于我来说,在使用Sun Java插件的IE中,调用applet上的任何方法都可以允许触发事件并重新输入脚本.这总是一个对时间敏感的错误,而Sun可能已经修复了它(我当然希望如此).
可能更多.自从我测试了这个版本已经有一段时间了,浏览器从此开始变得复杂.
总之,大多数用户在大多数情况下都会看到JavaScript具有严格的事件驱动的单个执行线程.实际上,它没有这样的东西.目前尚不清楚这有多少只是一个错误和多少刻意的设计,但如果你正在编写复杂的应用程序,特别是跨窗口/框架脚本的应用程序,它有可能咬你吗? - ?间歇性的,难以调试的方式.
如果最坏的情况发生,您可以通过间接所有事件响应来解决并发问题.当一个事件进入时,将其放入一个队列中,然后在setInterval函数中按顺序处理该队列.如果您正在编写一个您打算被复杂应用程序使用的框架,那么这样做可能是一个很好的举措.postMessage也希望能够缓解未来跨文档脚本的痛苦.
Már*_*son 113
我会说是 - 因为如果浏览器的javascript引擎以异步方式运行它,几乎所有现有的(至少所有非平凡的)javascript代码都会中断.
除此之外,HTML5已经指定Web Workers(一种用于多线程javascript代码的显式,标准化API)这一事实将多线程引入基本Javascript中将是毫无意义的.
(注意其他评论者:尽管setTimeout/setIntervalHTTP请求onload事件(XHR)和UI事件(点击,焦点等)提供了多线程的粗糙印象 - 它们仍然沿着一个时间线执行 - 一个在一段时间 - 即使我们事先不知道他们的执行顺序,也不必担心在执行事件处理程序,定时函数或XHR回调期间外部条件发生变化.)
JavaScript/ECMAScript旨在生活在主机环境中.也就是说,除非主机环境决定解析并执行给定的脚本,并且提供允许JavaScript实际有用的环境对象(例如浏览器中的DOM),否则JavaScript实际上不会执行任何操作.
我认为给定的函数或脚本块将逐行执行,并且保证JavaScript.但是,主机环境可能同时执行多个脚本.或者,主机环境始终可以提供提供多线程的对象.setTimeout并且setInterval是主机环境的示例,或者至少是伪示例,提供了一种进行并发的方法(即使它不是完全并发).
@Bobince提供了一个非常不透明的答案。
忽略了MárÖrlygsson的回答,由于以下简单事实,Javascript始终是单线程的:Javascript中的所有内容都沿着单个时间轴执行。
那是单线程编程语言的严格定义。
我会说规范不会阻止某人创建在多个线程上运行javascript的引擎,要求代码执行同步以访问共享对象状态.
我认为单线程非阻塞范例来自于在ui永远不应该阻塞的浏览器中运行javascript的需要.
Nodejs遵循浏览器的方法.
但是,Rhino引擎支持在不同的线程中运行js代码.执行不能共享上下文,但它们可以共享范围.对于这个特定情况,文档说明:
......"Rhino保证对JavaScript对象的属性的访问在线程中是原子的,但不会对同时在同一范围内执行的脚本提供更多保证.如果两个脚本同时使用相同的范围,则脚本是负责协调对共享变量的任何访问."
从阅读Rhino文档我得出结论,有人可能编写一个javascript api,它也会生成新的javascript线程,但是api将是特定于rhino的(例如,节点只能生成一个新进程).
我想即使对于支持javascript中的多个线程的引擎,也应该与不考虑多线程或阻塞的脚本兼容.
以我看到的方式来构思浏览器和nodejs:
是否所有js代码都在一个线程中执行?:是的.
js代码可以导致其他线程运行吗?:是的.
这些线程是否可以改变js执行上下文?:否.但是它们可以(直接/间接(?))附加到事件队列中.
因此,在浏览器和nodejs(可能还有很多其他引擎)的情况下,javascript不是多线程的,但引擎本身就是.
| 归档时间: |
|
| 查看次数: |
118998 次 |
| 最近记录: |