KnockoutJS foreach 阻塞主线程

发布于 2024-12-15 16:40:33 字数 443 浏览 0 评论 0原文

当我的 viewModel 中有一个大型数据集并且我使用 foreach 循环对象数组以将每个对象渲染为表中的一行时,KnockoutJS 将阻塞主线程,直到它可以渲染为止。有时需要几分钟(!)。

这是一个 jsFiddle 示例,使用包含 2000 个对象的数据集,其中包含 urlcode。在某些情况下,真实数据将具有更长的 URL 和其他 4 列(本例中只有 2 列)。我还添加了一些简单的样式,因为添加样式似乎也会在此过程中减慢速度。

警告:您的浏览器可能崩溃

http://jsfiddle.net/DESC3/7/< /a>

When I have a large dataset in my viewModel and I use foreach to loop over an Array of Objects to render each Object as a row within a table, KnockoutJS will block the main thread until it can render, which sometimes takes minutes (!).

Here is a jsFiddle example using a dataset containing 2000 Objects, containing a url and a code. Real data will have longer URLs in some cases and 4 other columns (only 2 in this example.) I also added some simple styles because adding styles also seems to slow things down a bit during the process.

Warning: your browser might break

http://jsfiddle.net/DESC3/7/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

神回复 2024-12-22 16:40:33

我建议如果您有如此大的数据集,请尝试替代解决方案。例如,slickGrid 通过仅为数据生成 HTML 元素,以更有效的方式呈现大型数据集。实际上可见。我们已将其用于大型数据集,并且表现良好。

I suggest that if you have such large datasets you try an alternative solution. For example slickGrid renders large datasets in a much more efficient way, by only generating HTML elements for the data that is actually visible. We've used this for large datasets, and it performs well.

帝王念 2024-12-22 16:40:33

像这样的事情怎么样?比如说,您想要渲染 viewModel.items = ko.observableArray()

  1. 拥有一个包含所有数据的单独的不可观察数组:var itemsToRender = functionThatReturnsLargeArray()
  2. itemsToRender 中的部分数据放入可观察数组中。假设只有 50 个元素。
  3. setTimeout 回调中不断将元素添加到可观察数组中。

注意1:您可以在 setTimeout 回调中添加一些时间跟踪,并增加/减少每次迭代时添加的项目数量。您的目标是将每次回调时间保持在 50-100 毫秒以下,以便您的应用程序仍然具有响应能力。

var batchSize = 50; // default number of items rendered per iteration
var batchOffset = 0;
function render(items, itemsToRender, done) {
    setTimeout(function () {
        var startTime = new Date().getTime();
        items.pushAll(itemsToRender.slice(batchOffset, batchSize));
        batchOffset += batchSize;
        // at this point Knockout rendered next batchSize items from itemsToRender
        var endTime = new Date().getTime();
        // update batchSize for next iteration
        batchSize = batchSize * 50 / (endTime - startTime); // 50 milliseconds
        batchSize = Math.min(itemsToRender.length, batchOffset + batchSize);
        if (batchSize > 0) render() else done(); // callback if you need one
    }, 0);
}
/* I haven't actually tested the code */

另一种批量大小更新策略可以基于目标 FPS。假设您希望达到 60 fps 的更新速率,因此每 1000 毫秒调用 setTimeout 60 次。处理整个集合需要更长的时间。您还可以使用 requestAnimationFrame 代替 setTimeout 并查看效果如何。

编辑构建-in throttting 已添加到 Knockout JS 中1.3(目前处于测试阶段,但看起来相当稳定)。


注意2:如果视图上的某些其他数据依赖于viewModel.items,您仍然可以将其映射到原始数组itemsToRender。例如,假设您想要显示集合中的项目数。如果您使用 viewModel.items().length,您最终会在 UI 中更改大小值,同时更多项目会被渲染。为了避免这种情况,您可以首先将大小绑定定义为基于 itemsToRenderdependentObservable,而不是 viewModel.items。渲染完所有项目后,如果您认为合适,可以将其重新映射到 viewModel.items 上。

How about something like this. Say, you've got viewModel.items = ko.observableArray() that you'd like to render.

  1. Have a separate non-observable array of all your data: var itemsToRender = functionThatReturnsLargeArray().
  2. Put some portion of your data from itemsToRender into your observable array. Say, 50 elements only.
  3. Keep adding elements into observable array in portions inside a setTimeout callback.

NOTE1: You can add some time-tracking into setTimeout callback and increase/reduce the number of items that you add at each iteration. Your goal is to keep each callback time below 50-100 milliseconds so your application still feels responsive.

var batchSize = 50; // default number of items rendered per iteration
var batchOffset = 0;
function render(items, itemsToRender, done) {
    setTimeout(function () {
        var startTime = new Date().getTime();
        items.pushAll(itemsToRender.slice(batchOffset, batchSize));
        batchOffset += batchSize;
        // at this point Knockout rendered next batchSize items from itemsToRender
        var endTime = new Date().getTime();
        // update batchSize for next iteration
        batchSize = batchSize * 50 / (endTime - startTime); // 50 milliseconds
        batchSize = Math.min(itemsToRender.length, batchOffset + batchSize);
        if (batchSize > 0) render() else done(); // callback if you need one
    }, 0);
}
/* I haven't actually tested the code */

Another batch size updating strategy could be based on target FPS. Say you'd like to achieve 60 fps update rate and thus 60 calls to setTimeout per 1000 milliseconds. That would take somewhat longer to process the whole collection. You can also use requestAnimationFrame instead of setTimeout and see how that would work out.

EDIT: Build-in throttling was added into Knockout JS 1.3 (currently it's in beta but seems pretty stable).


NOTE2: If some other data on the view depends on viewModel.items you can still map it down to original array itemsToRender. Say, for example, that you'd like to show the number of items in collection. If you use viewModel.items().length you'll end up with changing size value in the UI while more items get renderred. To avoid that you can first define your size binding as a dependentObservable based on itemsToRender, not viewModel.items. After you've done rendering all items you can re-map it onto viewModel.items if you see fit.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文