为了账号安全,请及时绑定邮箱和手机立即绑定

JavaScript是否保证是单线程的?

JavaScript是否保证是单线程的?

幕布斯6054654 2019-05-31 17:06:11
JavaScript是否保证是单线程的?众所周知,JavaScript在所有现代浏览器实现中都是单线程的,但它是在任何标准中指定的,还是仅仅是传统的呢?假设JavaScript始终是单线程的,这完全安全吗?
查看完整描述

4 回答

?
烙印99

TA贡献1829条经验 获得超13个赞

这是个好问题。我很想说“是”。我不行。


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


(*:忽略浏览器是否真的使用一个OS线程来实现他们的JS引擎,或者其他有限的执行线程是否是由WebWorker引入的问题)。


然而,在现实中不完全是真的以卑鄙的方式。


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


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();

<textarea id="log" rows="20" cols="40"></textarea>

<input id="inp">

结果log in, blur, log out除了IE。这些事件不只是因为你打电话focus()直接的,他们可能会发生,因为你打电话给alert(),或者打开一个弹出窗口,或者其他任何移动焦点的东西。


这也可能导致其他事件。例如,添加一个i.onchange侦听器并在输入中键入focus()调用没有焦点,日志顺序是log in, change, blur, log out,除了在歌剧院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';

};

<textarea id="log" rows="20" cols="40"></textarea>

<button id="act">alert</button>

命中alert你会得到一个模态对话框。除非你拒绝那个对话,否则不会再执行剧本了,是吗?没有。调整主窗口的大小,您将得到alert in, resize, alert out在文本区域。


您可能会认为,在模式对话框打开时,不可能调整窗口的大小,但并非如此:在Linux中,您可以任意调整窗口的大小;在Windows上,这并不容易,但您可以通过将屏幕分辨率从更大的更改为不适合窗口的更小的分辨率来实现,从而调整窗口的大小。


你可能会想,嗯,这只是resize(可能还有更多的scroll),因为脚本是线程的,所以当用户没有与浏览器进行主动交互时,它就会触发。对于单一窗口,你可能是对的。但是,当你做跨窗口脚本时,这一切都会被抛到脑后。对于除Safari之外的所有浏览器,在任何一个浏览器繁忙时都会阻塞所有窗口/选项卡/框架,您可以从另一个文档的代码中与文档交互,运行在单独的执行线程中,并导致任何相关的事件处理程序被触发。

可以在脚本仍然线程化的情况下引发您可以导致生成的事件的地方:

  • 当模态弹出时(alertconfirmprompt在除Opera之外的所有浏览器中都是开放的;

  • 期间showModalDialog在支持它的浏览器上;

  • “这页上的剧本可能很忙.”对话框,即使你选择让脚本继续运行,允许像调整大小和模糊事件触发和处理,即使脚本是在繁忙的循环中间,除了在Opera。

  • 不久前,对于我来说,在IE中使用Sun Java插件,调用applet上的任何方法都可能允许事件触发并重新输入脚本。这一直是一个时间敏感的错误,有可能Sun已经修复了它(我当然希望如此)。

  • 可能更多。我已经有一段时间没有对此进行测试了,从那时起浏览器就变得越来越复杂了。

总之,在大多数用户看来,JavaScript似乎有一个严格的事件驱动的单线程执行。实际上,它没有这样的东西。目前还不清楚这其中有多少只是一个bug,有多少是刻意设计的,但如果你在编写复杂的应用程序,特别是跨窗口/框架脚本的应用程序,它很有可能会咬你一口-而且是间歇性的、难以调试的方式。

如果最坏的情况发生在最坏的情况下,您可以通过引导所有事件响应来解决并发问题。当事件出现时,将其放到队列中,然后按顺序处理队列,然后在setInterval功能。如果您正在编写一个您想要被复杂应用程序使用的框架,那么这样做可能是一个不错的举动。postMessage也有希望在未来缓解跨文档脚本的痛苦。


查看完整回答
反对 回复 2019-05-31
?
哆啦的时光机

TA贡献1779条经验 获得超6个赞

我会说是的,因为如果浏览器的javascript引擎异步运行,几乎所有现存的(至少是所有重要的)javascript代码都会中断。

再加上一个事实HTML 5已经指定了Web工作者(一个明确的、标准化的多线程javascript代码API)在基本Javascript中引入多线程基本上是没有意义的。

(给其他人的评论:即使setTimeout/setInterval,HTTP-请求onload事件(XHR)和UI事件(单击、焦点等)给人一种简单的多重性印象-它们仍然都是沿着一个时间线执行的-一次一条-所以即使我们事先不知道它们的执行顺序,也没有必要担心事件处理程序、时间函数或XHR回调的执行过程中外部条件的变化。)


查看完整回答
反对 回复 2019-05-31
?
繁花不似锦

TA贡献1851条经验 获得超4个赞

是的,尽管在使用任何异步API(如setInterval和xmlhttp回调)时仍然会遇到并发编程的一些问题(主要是争用条件)。


查看完整回答
反对 回复 2019-05-31
?
慕仙森

TA贡献1827条经验 获得超8个赞

是的,虽然InternetExplorer 9将在一个单独的线程上编译Javascript,以准备在主线程上执行。不过,作为一个程序员,这不会改变任何事情。


查看完整回答
反对 回复 2019-05-31
  • 4 回答
  • 0 关注
  • 544 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信