javascript 有没有办法在 Web Worker 中创建 DOM 元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18056922/
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-10-27 10:36:40  来源:igfitidea点击:

Is there a way to create out of DOM elements in Web Worker?

javascriptjqueryhtmlweb-worker

提问by Joren Van Severen

Context:I have a web application that processes and shows huge log files. They're usually only about 100k lines long, but it can be up to 4 million lines or more. To be able to scroll through that log file (both user initiated and via JavaScript) and filter the lines with decent performance I create a DOM element for each line as soon as the data arrives (in JSON via ajax). I found this better for performance then constructing the HTML at the back-end. Afterwards I save the elements in an array and I only show the lines that are visible.

上下文:我有一个 Web 应用程序,可以处理和显示巨大的日志文件。它们通常只有大约 100k 行,但可以达到 400 万行或更多。为了能够滚动浏览该日志文件(用户启动的和通过 JavaScript 的)并以良好的性能过滤行,我在数据到达时为每行创建一个 DOM 元素(通过 ajax 以 JSON 格式)。我发现这比在后端构建 HTML 的性能更好。之后我将元素保存在一个数组中,并且只显示可见的行。

For max 100k lines this takes only about a few seconds, but anything more takes up to one minute for 500k lines (not including the download). I wanted to improve the performance even more, so I tried using HTML5 Web Workers. The problem now is that I can't create elements in a Web Worker, not even outside the DOM. So I ended up doing only the json to HTML conversion in the Web Workers and send the result to the main thread. There it is created and stored in an array. Unfortunately this worsened the performance and now it takes at least 30 seconds more.

对于最多 100k 行,这只需大约几秒钟,但对于 500k 行(不包括下载),更多需要一分钟。我想进一步提高性能,所以我尝试使用 HTML5 Web Workers。现在的问题是我无法在 Web Worker 中创建元素,甚至在 DOM 之外也不行。所以我最终只在 Web Workers 中进行了 json 到 HTML 的转换,并将结果发送到主线程。在那里它被创建并存储在一个数组中。不幸的是,这使性能恶化,现在至少需要 30 秒以上。

Question:So is there any way, that I'm not aware of, to create DOM elements, outside the DOM tree, in a Web Worker? If not, why not? It seems to me that this can't create concurrency problems, as creating the elements could happen in parallel without problems.

问题:那么有什么我不知道的方法可以在 DOM 树之外的 Web Worker 中创建 DOM 元素?如果没有,为什么不呢?在我看来,这不会造成并发问题,因为创建元素可以毫无问题地并行发生。

采纳答案by Joren Van Severen

Alright, I did some more research with the information @Bergi provided and found the following discussion on W3C mailing list:

好的,我对@Bergi 提供的信息进行了更多研究,并在 W3C 邮件列表中找到了以下讨论:

http://w3-org.9356.n7.nabble.com/Limited-DOM-in-Web-Workers-td44284.html

http://w3-org.9356.n7.nabble.com/Limited-DOM-in-Web-Workers-td44284.html

And the excerpt that answers why there is no access to the XML parser or DOM parser in the Web Worker:

以及回答为什么无法访问 Web Worker 中的 XML 解析器或 DOM 解析器的摘录:

You're assuming that none of the DOM implementation code uses any sort of non-DOM objects, ever, or that if it does those objects are fully threadsafe. That's just not not the case, at least in Gecko.

The issue in this case is not the same DOM object being touched on multiple threads. The issue is two DOM objects on different threads both touching some global third object.

For example, the XML parser has to do some things that in Gecko can only be done on the main thread (DTD loading, offhand; there are a few others that I've seen before but don't recall offhand).

您假设所有 DOM 实现代码都不会使用任何类型的非 DOM 对象,或者如果确实如此,这些对象是完全线程安全的。事实并非如此,至少在 Gecko 中是如此。

这种情况下的问题不是在多个线程上触及同一个 DOM 对象。问题是不同线程上的两个 DOM 对象都接触某个全局第三个对象。

例如,XML 解析器必须做一些在 Gecko 中只能在主线程上完成的事情(DTD 加载,临时的;还有一些我以前见过但不记得是临时的)。

There is however also a workaround mentioned, which is using a third-party implementation of the parsers, of which jsdomis an example. With this you even have access to your own separate Document.

然而,也提到了一种解决方法,它使用解析器的第三方实现,其中jsdom就是一个例子。有了这个,您甚至可以访问自己的单独文档。

回答by Bergi

So is there any way, that I'm not aware of, to create DOM elements, outside the DOM tree, in a Web Worker?

那么有什么我不知道的方法可以在 DOM 树之外的 Web Worker 中创建 DOM 元素?

No.

不。

Why not? It seems to me that this can't create concurrency problems, as creating the elements could happen in parallel without problems.

为什么不?在我看来,这不会造成并发问题,因为创建元素可以毫无问题地并行发生。

Not for creating them, you're right. But for appending them to the main document- they would need to be sent to a different memory (like it's possible for blobs) so that they're inaccessible from the worker thereafter. However, there's absolutely no Document handling available in WebWorkers.

不是为了创造它们,你是对的。但是为了将它们附加到 main document- 它们需要被发送到不同的内存(就像 blob 可能的那样),以便此后工作人员无法访问它们。但是,WebWorkers 中绝对没有可用的文档处理

I create a DOM element for each line as soon as the data arrives (in JSON via ajax). Afterwards I save the elements in an array and I only show the lines that are visible.

数据一到达,我就为每一行创建一个 DOM 元素(通过 ajax 在 JSON 中)。之后我将元素保存在一个数组中,并且只显示可见的行。

Constructing over 500k DOM elements is the heavy task. Try to create DOM elements only for the lines that are visible. To improve performance and showing the first few lines faster, you also might chunk their processing into smaller units and use timeouts in between. See How to stop intense Javascript loop from freezing the browser

构建超过 50 万个 DOM 元素是一项繁重的任务。尝试只为可见的行创建 DOM 元素。为了提高性能并更快地显示前几行,您还可以将它们的处理分成更小的单元并在它们之间使用超时。请参阅如何阻止密集的 Javascript 循环冻结浏览器

回答by Aron Allen

There is no direct way to access the DOM through Web Workers. I recently released @cycle/sandbox, it is still WIP, but it proves with the Cycle JS architecture it is fairly straight forward to declare UI behaviour in the Web Worker. The actual DOM is only touched in the main thread, but event listeners, and DOM updates are indirectly declared in the worker, and a synthesized event object is sent when something happens on those listeners. Furthermore it is straight forward to mount these sandboxed Cycle Components side-by-side regular Cycle Components.

没有通过 Web Workers 直接访问 DOM 的方法。我最近发布了@cycle/sandbox,它仍然是 WIP,但它证明了 Cycle JS 架构在 Web Worker 中声明 UI 行为是相当直接的。实际的 DOM 只在主线程中被触及,但事件监听器和 DOM 更新是在工作线程中间接声明的,当这些监听器发生某些事情时,会发送一个合成的事件对象。此外,直接将这些沙盒循环组件与常规循环组件并排安装。

http://github.com/aronallen/-cycle-sandbox/

http://github.com/aronallen/-cycle-sandbox/

回答by Halcyon

You have to understand the nature of a webworker. Programming with threads is hard, especially if you're sharing memory; weird things can happen. JavaScript is not equipped to deal with any kind of thread-like interleaving.

你必须了解网络工作者的本质。使用线程编程很困难,尤其是在共享内存的情况下;奇怪的事情可能会发生。JavaScript 无法处理任何类型的类似线程的交错。

The approach of webworkers is that there is no shared memory. This obviously leads to the conclusion that you can't access the DOM.

网络工作者的做法是没有共享内存。这显然会导致无法访问 DOM 的结论。

回答by posit labs

I don't see any reason why you can't construct html strings using web-workers. But I also don't think there would be much of a performance boost.

我看不出有什么理由不能使用 web-workers 构造 html 字符串。但我也不认为会有太大的性能提升。

This isn't related to Web-Workers, but it relates to the problem you're trying to solve. Here are some thing that might help speed things up:

这与 Web-Workers 无关,但与您要解决的问题有关。以下是一些可能有助于加快速度的事情:

  1. Use DocumentFragments. Add elements to them as the data comes in, and add the fragments to the DOM at an interval (like once a second). This way you don't have to touch the DOM (and incur a redraw) every time a line of text is loaded.

  2. Do loading in the background, and only parse the lines as the user hits the bottom of the scroll area.

  1. 使用文档片段。当数据进来时向它们添加元素,并每隔一段时间(比如每秒一次)将片段添加到 DOM 中。这样您就不必在每次加载一行文本时都触摸 DOM(并导致重绘)。

  2. 在后台加载,并且只在用户点击滚动区域底部时解析行。

回答by Strille

According to https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workersthere's no access to the DOM from a web worker unfortunately.

根据https://developer.mozilla.org/en-US/docs/Web/Guide/Performance/Using_web_workers不幸的是,网络工作者无法访问 DOM。

回答by Dominic Cerisano

You have a couple of anti-patterns in your design:

您的设计中有几个反模式:

  1. Creating a DOM object has considerable overhead, and you are creating potentially millions of them at once.
  2. Trying to get a web worker to manage the DOM is exactly what web workers are not for. They do everything else so the DOM event loop stays responsive.
  1. 创建 DOM 对象具有相当大的开销,而且您可能同时创建数百万个对象。
  2. 试图让 Web Worker 管理 DOM 正是 Web Worker 所不具备的。他们做其他所有事情,以便 DOM 事件循环保持响应。

You can use a cursor pattern to scroll through arbitrarily largesets of data.

您可以使用光标模式滚动浏览任意大的数据集。

  1. DOM posts a message to worker with start position and number of lines requested (cursor).
  2. Web worker random accesses logs, posts back the fetched lines (cursor data).
  3. DOM updates an element with the async cursor response event.
  1. DOM 向工作人员发布一条消息,其中包含开始位置和请求的行数(光标)。
  2. Web worker 随机访问日志,回发获取的行(游标数据)。
  3. DOM 使用异步光标响应事件更新元素。

This way, the heavy lifting is done by the worker, whose event loop is blocked during the fetch instead of the DOM, resulting in happy non-blocked users marvelling at how smooth all your animations are.

这样,繁重的工作由工作人员完成,其事件循环在获取期间被阻塞而不是 DOM,导致快乐的非阻塞用户惊叹您所有动画的流畅程度。

回答by user316264

No you can't create DOM elements in a web worker, but you can create a function that accepts the post message from that web worker, that doescreate the DOM elements. I think the deign that your looking for is called array chucking. And you would need to mix that with the web worker design pattern.

不,您不能在 Web Worker 中创建 DOM 元素,但是您可以创建一个函数来接受来自该 Web Worker 的 post 消息,从而does创建 DOM 元素。我认为您正在寻找的设计称为阵列卡盘。您需要将其与 Web Worker 设计模式混合使用。

回答by Fordi

So you can't directly create DOM in a webworker - however, there may be another option to do a fair bit of your processing outside the main thread.

所以你不能直接在 webworker 中创建 DOM - 但是,可能还有另一种选择可以在主线程之外进行大量处理。

Check out this jsPerf I just created: http://jsperf.com/dom-construction-obj-vs-str

查看我刚刚创建的这个 jsPerf:http://jsperf.com/dom-construction-obj-vs-str

Essentially, you couldbe emitting POJSO's that have all the same values you get from a DOM, and convert it to DOM objects after receiving the message (this is what you're doing when you get HTML back, after all; POJSOs are just lower overhead, by virtue of not requiring further string processing). In this way you could even do things like emit event listeners and such (by, say, prefixing the event name with '!', and having the value map to some template-supplied view argument).

从本质上讲,您可以发出具有从 DOM 中获得的所有相同值的 POJSO,并在收到消息后将其转换为 DOM 对象(毕竟这是您在返回 HTML 时所做的事情;POJSO 只是较低开销,因为不需要进一步的字符串处理)。通过这种方式,您甚至可以执行诸如发出事件侦听器之类的操作(例如,在事件名称前加上“!”,并将值映射到某个模板提供的视图参数)。

Meanwhile, without the DOM parser available, you'll need your own thing to convert a template as-needed, or to compile one to a format that's fast.

同时,如果没有可用的 DOM 解析器,您将需要自己的东西来根据需要转换模板,或者将模板编译为快速的格式。