为什么JavaScript在常见的浏览器中没有自己的线程?

use*_*777 15 javascript browser architecture software-design

JavaScript不是多线程的,显然JavaScript甚至没有自己的,但是与其他东西共享一个线程.即使在大多数现代浏览器中,JavaScript通常与绘画,更新样式和处理用户操作位于同一队列中.

这是为什么?

根据我的经验,如果JavaScript在自己的线程上运行,可以获得极大改善的用户体验,单独通过JS不阻止UI呈现或解放复杂或有限的消息队列优化样板(是的,也是你,webworkers!),开发人员已经写它自己以保持UI在整个地方的响应,当它真正归结为它.

我有兴趣理解控制这种看似不幸的设计决策的动机,从软件架构的角度来看,有一个令人信服的理由吗?

jfr*_*d00 18

用户操作需要JS事件处理程序的参与

用户操作可以触发参与并可能影响用户操作的Javascript事件(点击,焦点事件,关键事件等等),因此在处理用户操作时,单个JS线程无法执行,因为如果是,然后JS线程无法参与用户操作,因为它已经在做其他事情了.因此,在JS线程可用于参与该进程之前,浏览器不会处理默认用户操作.

渲染

渲染更复杂.一个典型的DOM修改序列如下:1)由JS修改的DOM,布局标记为脏,2)JS线程完成执行,因此浏览器现在知道JS完成了修改DOM,3)浏览器做布局以重新布局更改的DOM,4 )浏览器根据需要绘制屏幕.

步骤2)在这里很重要.如果浏览器在每次JS DOM修改后都进行了新的布局和屏幕绘制,那么如果JS实际上要进行一堆DOM修改,那么整个过程可能会非常低效.此外,还会出现线程同步问题,因为如果您在浏览器尝试重新布局和重新绘制的同时让JS修改DOM,则必须同步该活动(例如阻止某人以便操作可以在没有基础数据被另一个线程改变了).

仅供参考,有一些解决方法可用于强制重新布局或强制从JS代码中重新绘制(不完全是你所要求的,但在某些情况下很有用).

多线程访问DOM真的很复杂

DOM本质上是一个大的共享数据结构.浏览器在解析页面时构造它.然后加载脚本和各种JS事件有机会修改它.

如果您突然有多个JS线程可以同时访问DOM,那么您将遇到一个非常复杂的问题.你会如何同步访问?您甚至无法编写最基本的DOM操作,该操作涉及在页面中查找DOM对象然后修改它,因为这不是原子操作.在您找到DOM对象和修改时间之间,DOM可能会发生变化.相反,您可能必须至少获取DOM中的子树的锁定,以防止在您操作或搜索它时由其他某个线程更改它.然后,在进行修改之后,您必须释放锁并从代码中释放任何关于DOM状态的知识(因为一旦释放锁,其他一些线程就可以更改它).并且,如果你没有正确地做事,你最终可能会遇到死锁或各种令人讨厌的错误.实际上,您必须将DOM视为并发的多用户数据存储区.这将是一个复杂得多的编程模型.

避免复杂性

"单线程JS"设计决策中有一个统一的主题. 保持简单.不需要了解多线程环境和线程同步工具以及调试多个线程,以便编写可靠,可靠的浏览器Javascript.

浏览器Javascript是一个成功的平台的一个原因是因为它对所有级别的开发人员都非常容易访问,并且相对容易学习和编写可靠的代码.虽然浏览器JS可能会随着时间的推移获得更多高级功能(就像我们使用WebWorkers一样),但您可以绝对肯定这些功能将以简单易懂的方式完成,而更高级的开发人员可以完成更高级的功能,但是没有打破任何让事情变得简单的事情.

仅供参考,我在node.js中编写了一个多用户Web服务器应用程序,由于nodejs Javascript的单线程特性,我不断惊讶于服务器设计的复杂程度.是的,有一些事情更难以编写(学习编写大量异步代码的承诺),但是简单的假设是你的JS代码永远不会被另一个请求中断,这大大简化了设计,测试并减少了很难找到并修复并发设计和编码总是充满的错误.

讨论

当然,第一个问题可以通过允许用户操作事件处理程序在自己的线程中运行来解决,以便它们可以随时发生.但是,然后你立即拥有多线程Javascript,现在需要一个全新的JS基础架构来进行线程同步和全新的bug类.浏览器Javascript的设计者一直决定不打开那个盒子.

如果需要,可以改进渲染问题,但是浏览器代码的复杂性很大.你必须发明一些方法来猜测当运行的JS代码似乎不再改变DOM时(可能有一些ms经过而没有更多的更改),因为你必须避免做重新布局并立即进行屏幕绘制每个DOM的变化.如果浏览器这样做,一些JS操作将比现在慢100倍(100x是一个疯狂的猜测,但关键是他们会慢很多).而且,你必须在布局,绘图和JS DOM修改之间实现线程同步,这是可行的,但很复杂,很多工作和浏览器实现错误的肥沃土壤.并且,当你通过重新布局或重新绘制并且JS线程进行DOM修改时,你必须决定做什么(没有一个答案很好).