46

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

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

问:那么有什么我不知道的方法可以在 DOM 树之外的 Web Worker 中创建 DOM 元素吗?如果不是,为什么不呢?在我看来,这不会产生并发问题,因为创建元素可以并行发生而不会出现问题。

4

9 回答 9

17

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

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

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

您假设没有任何 DOM 实现代码使用任何类型的非 DOM 对象,或者如果它使用这些对象是完全线程安全的。情况并非如此,至少在 Gecko 中是这样。

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

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

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

于 2013-08-07T11:44:16.130 回答
12

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

不。

为什么不?在我看来,这不会产生并发问题,因为创建元素可以并行发生而不会出现问题。

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

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

构建超过 500k 的 DOM 元素是一项艰巨的任务。尝试仅为可见的行创建 DOM 元素。为了提高性能并更快地显示前几行,您还可以将它们的处理分成更小的单元并在其间使用超时。请参阅如何阻止激烈的 Javascript 循环冻结浏览器

于 2013-08-06T16:58:59.210 回答
4

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

webworkers 的做法是没有共享内存。这显然会导致您无法访问 DOM 的结论。

于 2013-08-05T11:29:38.170 回答
4

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

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

于 2017-04-25T07:45:34.497 回答
3

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

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

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

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

于 2013-08-05T16:58:43.310 回答
1

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

于 2013-08-05T11:24:39.140 回答
1

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

  1. 创建一个 DOM 对象有相当大的开销,而且您可能同时创建数百万个
  2. 试图让 web worker 来管理 DOM 正是 web worker不适合的。他们做所有其他事情,以便 DOM 事件循环保持响应。

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

  1. DOM 向工作人员发送一条消息,其中包含起始位置和请求的行数(光标)。
  2. Web Worker 随机访问日志,回发获取的行(光标数据)。
  3. DOM 使用异步光标响应事件更新元素。

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

于 2016-06-12T06:30:45.767 回答
0

因此,您不能直接在 webworker 中创建 DOM - 但是,可能还有另一种选择可以在主线程之外进行相当多的处理。

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

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

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

于 2016-02-23T22:44:16.913 回答
0

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

于 2016-07-20T08:30:09.073 回答