当前位置: 首页 > 面试题库 >

是否保证JavaScript是单线程的?

谷梁云瀚
2023-03-14
问题内容

众所周知,JavaScript在所有现代浏览器实现中都是单线程的,但是它是在任何标准中指定的,还是仅根据传统?假设JavaScript始终是单线程的,是否完全安全?


问题答案:

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

通常认为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();


<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,除了Opera所在的位置log in, blur, log out, change和IE所在的位置(甚至更少)log in, change, log out, blur

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

(我在这里使用直接on...事件处理程序属性,但addEventListenerand 也会发生同样的情况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)是开放的,在所有的浏览器,但歌剧;

  • showModalDialog支持它的浏览器中;

  • 即使您选择让脚本继续运行,“此页面上的脚本可能正忙于…”对话框也将允许触发大小调整和模糊等事件,即使该脚本位于脚本中间,也可以对其进行处理。忙循环,Opera除外。

  • 对我来说,前一段时间,在带有Sun Java Plugin的IE中,调用applet上的任何方法都可以触发事件并重新输入脚本。自始至终,这始终是一个对时间敏感的错误,Sun可能已经对此进行了修复(我当然希望如此)。

  • 可能更多。自从我测试了这已经有一段时间了,浏览器也因此变得越来越复杂。

总之,大多数时候,JavaScript对大多数用户来说似乎具有严格的事件驱动的单线程执行。实际上,它没有这样的东西。尚不清楚其中有多少只是一个错误,有多少是经过深思熟虑的设计,但是如果您要编写复杂的应用程序,尤其是跨窗口/框架脚本的应用程序,那么它很有可能会咬住您-
并且断断续续地,难以调试的方式。

如果情况变得最糟,则可以通过间接所有事件响应来解决并发问题。当事件进入时,将其放入队列中,然后在setInterval函数中按顺序处理该队列。如果您要编写打算供复杂应用程序使用的框架,那么这样做可能是一个不错的选择。postMessage希望将来也能缓解跨文档脚本编写的痛苦。



 类似资料:
  • 问题内容: 我对Javascript的单线程性质有疑问。 此代码的结果是。如您所见,这之后让我感到奇怪的是,在单线程环境中是否应该不应该这样做?如果没有,那么JS怎么知道第二个应该在第一个之前完成?难道不应该有两个线程可以同时工作以完成两个s以便进行通知吗? 问题答案: JavaScript(在浏览器中) 不能 同时运行2。 最多 一个 的的回调可以同时执行-因为有 一个 JavaScript执行

  • 问题内容: 考虑以下代码: 当第一个URL够用时会被要求输入第二个URL吗? 我尝试了一个较小的示例,它看起来像预期的那样工作。即一个一个地处理数据,但是可以依靠这种行为吗?如果没有,在帮助之前打电话吗? 输出: 更新 :如果对实施很重要,请使用官方Oracle JDK8 答案 :根据下面的评论和答案,flatmap部分是惰性的。即完全读取第一个流,并且仅在需要时才读取下一个。渴望读取一个流,但是

  • 问题内容: Scrapy中很少有并发设置,例如CONCURRENT_REQUESTS。这意味着Scrapy搜寻器是多线程的吗?因此,如果我运行它,将在字面上并行触发多个同时请求?我问是因为,我读过Scrapy是单线程的。 问题答案: Scrapy是单线程的,除了交互式外壳程序和一些测试之外,请参见source。 它建立在Twisted的基础上,Twisted也是单线程的,并且利用了它自己的异步并发

  • 问题内容: 如果我创建这样的对象: 生成的对象会 总是 这样吗? 也就是说,属性的顺序是否与我添加它们的顺序相同? 问题答案: 自ES2015起,对象的迭代顺序遵循一组特定的规则,但不(始终)遵循插入顺序。简而言之,迭代顺序是字符串键的插入顺序和数字键的升序的组合: 使用数组或Map对象可能是实现此目的的更好方法。Map与密钥共享一些相似之处Object并保证密钥按插入顺序进行迭代,无一例外: M

  • volatile的写操作,无法保证线程安全。例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1对count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

  • 问题内容: 我真的是在这里指身份平等。 例如,以下内容将始终打印 true 吗? 问题答案: 是的,类令牌是唯一的(对于任何给定的类加载器而言)。 即,您将始终 在相同的类加载器领域中 获得对相同物理对象的引用。但是,不同的类加载器将加载不同的类令牌,同时,当两个不同的类加载器加载相同的类定义时,会将相同的类定义视为不同。