JavaScript 是否保证是单线程的?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2734025/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:42:24  来源:igfitidea点击:

Is JavaScript guaranteed to be single-threaded?

javascriptconcurrency

提问by Egor Pavlikhin

JavaScript is known to be single-threaded in all modern browser implementations, but is that specified in any standard or is it just by tradition? Is it totally safe to assume that JavaScript is always single-threaded?

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

采纳答案by bobince

That's a good question. I'd love to say “yes”. I can't.

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

JavaScript is usually considered to have a single thread of execution visible to scripts(*), so that when your inline script, event listener or timeout is entered, you remain completely in control until you return from the end of your block or function.

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

(*: ignoring the question of whether browsers really implement their JS engines using one OS-thread, or whether other limited threads-of-execution are introduced by WebWorkers.)

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

However, in reality this isn't quite true, in sneaky nasty ways.

然而,实际上这并不完全正确,以偷偷摸摸的令人讨厌的方式。

The most common case is immediate events. Browsers will fire these right away when your code does something to cause them:

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

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">

Results in log in, blur, log outon all except IE. These events don't just fire because you called focus()directly, they could happen because you called alert(), or opened a pop-up window, or anything else that moves the focus.

log in, blur, log out除了 IE 之外的所有结果。这些事件不仅仅因为您focus()直接调用而触发,它们还可能因为您调用alert()、打开弹出窗口或任何其他移动焦点的东西而发生。

This can also result in other events. For example add an i.onchangelistener and type something in the input before the focus()call unfocuses it, and the log order is log in, change, blur, log out, except in Opera where it's log in, blur, log out, changeand IE where it's (even less explicably) log in, change, log out, blur.

这也可能导致其他事件。例如,添加一个i.onchange监听器并在focus()调用取消焦点之前在输入中输入一些内容,日志顺序是log in, change, blur, log out,除了在它所在的 Operalog in, blur, log out, change和 IE 所在的位置(甚至更不明确)log in, change, log out, blur

Similarly calling click()on an element that provides it calls the onclickhandler immediately in all browsers (at least this is consistent!).

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

(I'm using the direct on...event handler properties here, but the same happens with addEventListenerand attachEvent.)

(我在这里使用直接on...事件处理程序属性,但同样发生在addEventListener和 上attachEvent。)

There's also a bunch of circumstances in which events can fire whilst your code is threaded in, despite you having done nothingto provoke it. An example:

尽管您没有采取任何措施来激发它,但还有很多情况会在您的代码被线程化时触发事件。一个例子:

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>

Hit alertand you'll get a modal dialogue box. No more script executes until you dismiss that dialogue, yes? Nope. Resize the main window and you will get alert in, resize, alert outin the textarea.

点击alert,你会得到一个模态对话框。在您关闭该对话之前不会再执行脚本,是吗?不。调整主窗口的大小,您将进入alert in, resize, alert out文本区域。

You might think it's impossible to resize a window whilst a modal dialogue box is up, but not so: in Linux, you can resize the window as much as you like; on Windows it's not so easy, but you can do it by changing the screen resolution from a larger to a smaller one where the window doesn't fit, causing it to get resized.

您可能认为在模式对话框打开时调整窗口大小是不可能的,但事实并非如此:在 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.

您可能会想,只有resize(可能还有一些类似scroll)可以在用户没有与浏览器进行主动交互时触发,因为脚本是线程化的。对于单个窗口,您可能是对的。但是,只要您在进行跨窗口脚本编写,这一切就会付诸东流。对于除 Safari 之外的所有浏览器,当它们中的任何一个忙时,它会阻止所有窗口/选项卡/框架,您可以从另一个文档的代码与文档交互,在单独的执行线程中运行并导致任何相关的事件处理程序火。

Places where events that you can cause to be generated can be raised whilst script is still threaded:

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

  • when the modal popups (alert, confirm, prompt) are open, in all browsers but Opera;

  • during showModalDialogon 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.

  • 当模式弹出窗口 ( alert, confirm, prompt) 打开时,在除 Opera 之外的所有浏览器中;

  • showModalDialog支持它的浏览器上;

  • “此页面上的脚本可能正忙...”对话框,即使您选择让脚本继续运行,也允许触发和处理诸如调整大小和模糊之类的事件,即使脚本处于中间忙循环,除了在 Opera 中。

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

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

In summary, JavaScript appears to most users, most of the time, to have a strict event-driven single thread of execution. In reality, it has no such thing. It is not clear how much of this is simply a bug and how much deliberate design, but if you're writing complex applications, especially cross-window/frame-scripting ones, there is every chance it could bite you?—?and in intermittent, hard-to-debug ways.

总之,在大多数情况下,对于大多数用户来说,JavaScript 似乎具有严格的事件驱动单线程执行。实际上,它没有这样的东西。目前尚不清楚这其中有多少只是一个错误,有多少是经过深思熟虑的设计,但是如果您正在编写复杂的应用程序,尤其是跨窗口/框架脚本的应用程序,那么它很有可能会咬到您?间歇性的,难以调试的方式。

If the worst comes to the worst, you can solve concurrency problems by indirecting all event responses. When an event comes in, drop it in a queue and deal with the queue in order later, in a setIntervalfunction. If you are writing a framework that you intend to be used by complex applications, doing this could be a good move. postMessagewill also hopefully soothe the pain of cross-document scripting in the future.

如果最坏的情况发生,您可以通过间接所有事件响应来解决并发问题。当一个事件进来时,把它放到一个队列中,然后在一个setInterval函数中按顺序处理这个队列。如果您正在编写一个打算供复杂应用程序使用的框架,那么这样做可能是一个不错的举措。postMessage也有望在未来缓解跨文档脚本的痛苦。

回答by Már ?rlygsson

I'd say yes - because virtually all existing (at least all non-trivial) javascript code would break if a browser's javascript engine were to run it asynchronously.

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

Add to that the fact that HTML5 already specifies Web Workers(an explicit, standardized API for multi-threading javascript code) introducing multi-threading into the basic Javascript would be mostly pointless.

此外,HTML5 已经指定了 Web Workers(一种用于多线程 JavaScript 代码的明确的标准化 API),将多线程引入基本 Javascript 几乎毫无意义。

(Note to others commenters:Even though setTimeout/setInterval, HTTP-request onload events (XHR), and UI events (click, focus, etc.) provide a crude impression of multi-threadedness - they are still all executed along a single timeline - one at a time - so even if we don't know their execution order beforehand, there's no need to worry about external conditions changing during the execution of an event handler, timed function or XHR callback.)

其他评论者请注意:尽管setTimeout/setIntervalHTTP 请求加载事件 (XHR) 和 UI 事件(单击、焦点等)提供了多线程的粗略印象 - 它们仍然全部沿单个时间线执行 - 一个在一段时间 - 因此,即使我们事先不知道它们的执行顺序,也无需担心在执行事件处理程序、定时函数或 XHR 回调期间外部条件发生变化。)

回答by spender

Yes, although you can still suffer some of the issues of concurrent programming (mainly race conditions) when using any of the asynchronous APIs such as setInterval and xmlhttp callbacks.

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

回答by ChessWhiz

Yes, although Internet Explorer 9 will compile your Javascript on a separate thread in preparation for execution on the main thread. This doesn't change anything for you as a programmer, though.

是的,尽管 Internet Explorer 9 会在单独的线程上编译您的 Javascript,以准备在主线程上执行。但是,作为程序员,这不会改变您的任何内容。

回答by Marinos An

I would say that the specification does not preventsomeone from creating an enginethat runsjavascript on multiple threads, requiring the code to perform synchronization for accessing shared object state.

我要说的是,说明书中并没有防止有人从创建发动机运行JavaScript的上多个线程,要求代码以访问共享对象状态执行同步。

I think the single-threaded non-blocking paradigmcame out of the need to run javascript in browserswhere ui should never block.

我认为单线程非阻塞范式源于需要在 ui 永远不会阻塞的浏览器中运行 javascript 。

Nodejshas followed the browsers' approach.

Nodejs遵循了浏览器的方法

Rhinoengine however, supports running js code in different threads. The executions cannot share context, but they can share scope. For this specific case the documentation states:

然而,Rhino引擎支持在不同线程中运行 js 代码。执行不能共享上下文,但它们可以共享范围。对于这种特定情况,文档说明:

..."Rhino guarantees that accesses to properties of JavaScript objects are atomic across threads, but doesn't make any more guarantees for scripts executing in the same scope at the same time.If two scripts use the same scope simultaneously, the scripts are responsible for coordinating any accesses to shared variables."

...“Rhino 保证对 JavaScript 对象的属性的访问是跨线程原子的,但不会对同时在同一范围内执行的脚本做出更多保证。如果两个脚本同时使用相同的范围,则脚本是负责协调对共享变量的任何访问。”

From reading Rhino documentation I conclude that that it can be possible for someone to write a javascript api that also spawns new javascript threads, but the api would be rhino-specific (e.g. node can only spawn a new process).

通过阅读 Rhino 文档,我得出结论,有人可以编写一个 javascript api 来生成新的 javascript 线程,但该 api 将是 Rhino 特定的(例如,节点只能生成一个新进程)。

I imagine that even for an engine that supports multiple threads in javascript there should be compatibility with scripts that do not consider multi-threading or blocking.

我想即使对于在 javascript 中支持多线程的引擎,也应该与不考虑多线程或阻塞的脚本兼容。

Concearning browsersand nodejsthe way I see it is:

关于浏览器nodejs,我认为它是:

    1. Is all jscode executed in a single thread? : Yes.
    1. Can jscode cause other threads to run? : Yes.
    1. Can these threads mutate js execution context?: No.But they can (directly/indirectly(?)) append to the event queuefrom which listenerscan mutate execution context. But don't be fooled, listeners run atomically on the main threadagain.
    1. 难道所有的JS代码在执行单线程?:是的。
    1. JS代码造成其他线程运行?:是的。
    1. 这些线程可以改变 js 执行上下文吗?:不能但是它们可以(直接/间接(?))附加到事件队列中, 侦听器可以从中改变执行上下文。但是不要被愚弄,侦听器再次在主线程上原子地运行。

So, in case of browsers and nodejs (and probably a lot of other engines) javascript is not multithreaded but the engines themselves are.

因此,在浏览器和 nodejs(可能还有很多其他引擎)的情况下,javascript 不是多线程的,但引擎本身是.



Update about web-workers:

关于网络工作者的更新:

The presence of web-workers justifies further that javascript can be multi-threaded, in the sense that someone can create code in javascript that will run on a separate thread.

网络工作者的存在进一步证明了 javascript 可以是多线程的,从某种意义上说,有人可以在 javascript 中创建将在单独线程上运行的代码。

However: web-workers do not curry the problems of traditional threadswho can share execution context. Rules 2 and 3 above still apply, but this time the threaded code is created by the user (js code writer) in javascript.

然而:web-workers 不会解决可以共享执行上下文的传统线程的问题上面的规则 2 和 3 仍然适用,但这次线程代码是由用户(js 代码编写者)在 javascript 中创建的。

The only thing to consideris the number of spawned threads, from an efficiency(and not concurrency) point of view. See below:

效率(而不是并发)的角度来看,唯一需要考虑的是衍生线程的数量。见下文:

About thread safety:

关于线程安全

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 pointswith 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.

Worker 接口产生了真正的操作系统级线程,细心的程序员可能会担心,如果您不小心,并发性会在您的代码中产生“有趣”的效果。

但是,由于 web Worker 对与其他线程的通信点进行了精心控制,因此实际上很难导致并发问题。无法访问非线程安全组件或 DOM。并且您必须通过序列化对象将特定数据传入和传出线程。所以你必须非常努力地在你的代码中引起问题。



P.S.

聚苯乙烯

Besides theory, always be prepared about possible corner cases and bugs described on the accepted answer

除了理论之外,始终准备好接受的答案中描述的可能的极端情况和错误

回答by Bob

JavaScript/ECMAScript is designed to live within a host environment. That is, JavaScript doesn't actually do anythingunless the host environment decides to parse and execute a given script, and provide environment objects that let JavaScript actually be useful (such as the DOM in browsers).

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

I think a given function or script block will execute line-by-line and that is guaranteed for JavaScript. However, perhaps a host environment could execute multiple scripts at the same time. Or, a host environment could always provide an object that provides multi-threading. setTimeoutand setIntervalare examples, or at least pseudo-examples, of a host environment providing a way to do some concurrency (even if it's not exactly concurrency).

我认为给定的函数或脚本块将逐行执行,这对 JavaScript 是有保证的。但是,也许主机环境可以同时执行多个脚本。或者,宿主环境总是可以提供一个提供多线程的对象。setTimeout并且setInterval是主机环境的示例,或者至少是伪示例,提供了一种执行某些并发的方法(即使它不完全是并发)。

回答by kennebec

Actually, a parent window can communicate with child or sibling windows or frames that have their own execution threads running.

实际上,父窗口可以与运行自己的执行线程的子窗口或兄弟窗口或框架通信。

回答by wle8300

@Bobince is providing a really opaque answer.

@Bobince 提供了一个非常不透明的答案。

Riffing off of Már ?rlygsson's answer, Javascript is always single-threaded because of this simple fact: Everything in Javascript is executed along a single timeline.

根据 Már?rlygsson 的回答,Javascript 始终是单线程的,因为这个简单的事实:Javascript 中的所有内容都沿着单个时间线执行。

That is the strict definition of a single-threaded programming language.

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

回答by max

No.

不。

I'm going against the crowd here, but bear with me. A single JS script is intended to be effectivelysingle threaded, but this doesn't mean that it can't be interpreted differently.

我要在这里反对人群,但请耐心等待。单个 JS 脚本旨在有效地实现单线程,但这并不意味着它不能被不同地解释。

Let's say you have the following code...

假设您有以下代码...

var list = [];
for (var i = 0; i < 10000; i++) {
  list[i] = i * i;
}

This is written with the expectation that by the end of the loop, the list must have 10000 entries which are the index squared, but the VM could notice that each iteration of the loop does not affect the other, and reinterpret using two threads.

这是写的,期望在循环结束时,列表必须有 10000 个条目,这些条目是索引平方,但 VM 可以注意到循环的每次迭代不会影响另一个,并使用两个线程重新解释。

First thread

第一个线程

for (var i = 0; i < 5000; i++) {
  list[i] = i * i;
}

Second thread

第二个线程

for (var i = 5000; i < 10000; i++) {
  list[i] = i * i;
}

I'm simplifying here, because JS arrays are more complicated then dumb chunks of memory, but if these two scripts are able to add entries to the array in a thread-safe way, then by the time both are done executing it'll have the same result as the single-threaded version.

我在这里进行了简化,因为 JS 数组比笨拙的内存块更复杂,但是如果这两个脚本能够以线程安全的方式向数组添加条目,那么当两者都执行完毕时,它将有与单线程版本相同的结果。

While I'm not aware of any VM detecting parallelizable code like this, it seems likely that it could come into existence in the future for JIT VMs, since it could offer more speed in some situations.

虽然我不知道有任何 VM 检测到像这样的可并行化代码,但它似乎有可能在未来出现在 JIT VM 中,因为它可以在某些情况下提供更高的速度。

Taking this concept further, it's possible that code could be annotated to let the VM know what to convert to multi-threaded code.

进一步考虑这个概念,可以对代码进行注释,让 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;
}

Since Web Workers are coming to Javascript, it's unlikely that this... uglier system will ever come into existence, but I think it's safe to say Javascript is single-threaded by tradition.

由于 Web Workers 正在使用 Javascript,因此这个……更丑陋的系统不太可能出现,但我认为可以肯定地说 Javascript 是传统的单线程。

回答by Francisco Soto

Well, Chrome is multiprocess, and I think every process deals with its own Javascript code, but as far as the code knows, it is "single-threaded".

好吧,Chrome 是多进程的,我认为每个进程都处理自己的 Javascript 代码,但就代码而言,它是“单线程的”。

There is no support whatsoever in Javascript for multi-threading, at least not explicitly, so it does not make a difference.

Javascript 不支持多线程,至少没有明确支持,因此它没有任何区别。