简体   繁体   English

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

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

Context: I have a web application that processes and shows huge log files.上下文:我有一个 web 应用程序,它处理并显示巨大的日志文件。 They're usually only about 100k lines long, but it can be up to 4 million lines or more.它们通常只有大约 100k 行,但可以达到 400 万行或更多。 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).为了能够滚动浏览该日志文件(用户启动和通过 JavaScript)并过滤具有良好性能的行,我在数据到达后立即为每一行创建一个 DOM 元素(通过 ajax 在 JSON 中)。 I found this better for performance then constructing the HTML at the back-end.我发现这比在后端构建 HTML 的性能更好。 Afterwards I save the elements in an array and I only show the lines that are visible.之后我将元素保存在一个数组中,我只显示可见的行。

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).对于最多 100k 行,这只需大约几秒钟,但对于 500k 行(不包括下载),任何更多都需要一分钟。 I wanted to improve the performance even more, so I tried using HTML5 Web Workers.我想进一步提高性能,所以我尝试使用 HTML5 Web Workers。 The problem now is that I can't create elements in a Web Worker, not even outside the DOM.现在的问题是我无法在 Web Worker 中创建元素,甚至在 DOM 之外。 So I ended up doing only the json to HTML conversion in the Web Workers and send the result to the main thread.所以我最终只在 Web Workers 中将 json 转换为 HTML 并将结果发送到主线程。 There it is created and stored in an array.它在那里被创建并存储在一个数组中。 Unfortunately this worsened the performance and now it takes at least 30 seconds more.不幸的是,这使性能恶化,现在至少需要多花 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?问题:那么我不知道有什么方法可以在 Web Worker 中在 DOM 树之外创建 DOM 元素? 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.在我看来,这不会产生并发问题,因为创建元素可以并行发生而不会出现问题。

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. 您假设DOM实现代码中没有任何一个使用任何类型的非DOM对象,或者如果它实现了那些对象是完全线程安全的。 That's just not not the case, at least in Gecko. 事实并非如此,至少在Gecko。

The issue in this case is not the same DOM object being touched on multiple threads. 在这种情况下的问题不是在多个线程上触及的DOM对象。 The issue is two DOM objects on different threads both touching some global third object. 问题是不同线程上的两个DOM对象都触及一些全局第三对象。

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). 例如,XML解析器必须做一些Gecko只能在主线程上完成的事情(DTD加载,offhand;还有一些我之前见过但不记得的东西)。

There is however also a workaround mentioned, which is using a third-party implementation of the parsers, of which jsdom is an example. 然而,还提到了一种解决方法,它使用解析器的第三方实现,其中jsdom就是一个例子。 With this you even have access to your own separate Document. 有了这个,你甚至可以访问自己独立的文档。

So is there any way, that I'm not aware of, to create DOM elements, outside the DOM tree, in a Web Worker? 那么,我不知道在Web Worker中创建DOM元素之外的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. 但是为了将它们附加到主document - 它们需要被发送到不同的内存(就像blob一样),以便之后工作人员无法访问它们。 However, there's absolutely no Document handling available in WebWorkers . 但是, WebWorkers中绝对没有可用的文档处理

I create a DOM element for each line as soon as the data arrives (in JSON via ajax). 一旦数据到达,我就为每一行创建一个DOM元素(通过ajax以JSON形式)。 Afterwards I save the elements in an array and I only show the lines that are visible. 然后我将元素保存在一个数组中,我只显示可见的行。

Constructing over 500k DOM elements is the heavy task. 构建超过500k的DOM元素是一项艰巨的任务。 Try to create DOM elements only for the lines that are visible. 尝试仅为可见的行创建DOM元素。 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 请参阅如何阻止强大的Javascript循环冻结浏览器

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的结论。

There is no direct way to access the DOM through Web Workers. 没有通过Web Workers访问DOM的直接方法。 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. 我最近发布了@cycle / sandbox,它仍然是WIP,但它证明了使用Cycle JS架构,在Web Worker中声明UI行为是相当直接的。 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. 实际DOM仅在主线程中触及,但事件侦听器和DOM更新在worker中间接声明,并且在这些侦听器上发生某些事件时发送合成事件对象。 Furthermore it is straight forward to mount these sandboxed Cycle Components side-by-side regular Cycle Components. 此外,可以直接安装这些沙盒循环组件并排定期循环组件。

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

I don't see any reason why you can't construct html strings using web-workers. 我没有看到任何你无法使用web-worker构造html字符串的原因。 But I also don't think there would be much of a performance boost. 但我也认为不会有太大的性能提升。

This isn't related to Web-Workers, but it relates to the problem you're trying to solve. 这与Web-Workers无关,但它与您尝试解决的问题有关。 Here are some thing that might help speed things up: 以下是一些可能有助于加快速度的事情:

  1. Use DocumentFragments. 使用DocumentFragments。 Add elements to them as the data comes in, and add the fragments to the DOM at an interval (like once a second). 在数据进入时向它们添加元素,并以一定间隔(例如每秒一次)将片段添加到DOM。 This way you don't have to touch the DOM (and incur a redraw) every time a line of text is loaded. 这样,每次加载一行文本时,您都不必触摸DOM(并重新绘制)。

  2. Do loading in the background, and only parse the lines as the user hits the bottom of the scroll area. 在后台加载,只在用户点击滚动区域底部时解析这些行。

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

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 . 创建DOM对象具有相当大的开销,并且您可能同时创建数百万个对象。
  2. Trying to get a web worker to manage the DOM is exactly what web workers are not for . 试图让网络工作者管理DOM正是网络工作者所不适用的 They do everything else so the DOM event loop stays responsive. 他们做其他所有事情,因此DOM事件循环保持响应。

You can use a cursor pattern to scroll through arbitrarily large sets of data. 您可以使用光标模式滚动任意大的数据集。

  1. DOM posts a message to worker with start position and number of lines requested (cursor). DOM向工作人员发送消息,其中包含起始位置和请求的行数(光标)。
  2. Web worker random accesses logs, posts back the fetched lines (cursor data). Web worker随机访问日志,回发获取的行(游标数据)。
  3. DOM updates an element with the async cursor response event. 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,从而使快乐的非阻塞用户惊叹于所有动画的平滑程度。

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 could be 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). 从本质上讲,你可能会发出POJSO,这些POJSO具有从DOM获得的所有相同值,并在收到消息后将其转换为DOM对象(毕竟这是你回到HTML时所做的事情; POJSO只是更低开销,因为不需要进一步的字符串处理)。 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). 通过这种方式,您甚至可以执行诸如发出事件侦听器之类的操作(例如,使用'!'为事件名称添加前缀,并将值映射到某个模板提供的视图参数)。

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解析器,您将需要自己的东西来根据需要转换模板,或者将其编译为快速的格式。

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 does create the DOM elements. 不,你不能创建一个网络工作者的DOM元素,但你可以创建一个接受来自网络工作者的帖子消息的功能,即does创建DOM元素。 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设计模式混合使用。

Update for 2022 (actually available in chrome since 2018): 2022 年更新(自 2018 年起实际在 chrome 中可用):

If you are ok with displaying your logs in a canvas element, you could use the new OffscreenCanvas api.如果您可以在 canvas 元素中显示您的日志,您可以使用新的OffscreenCanvas api。

The OffscreenCanvas interface provides a canvas that can be rendered off screen. OffscreenCanvas 接口提供了一个可以离屏渲染的 canvas。 It is available in both the window and worker contexts.它在 window 和工作者上下文中都可用。

You could then asynchronously display frames produced in the Worker back to a canvas element on the main thread.然后,您可以将 Worker 中生成的帧异步显示回主线程上的 canvas 元素。

More examples here .更多例子在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM