淘汰赛订阅阻止了该页面 [英] Knockout subscribe is blocking the page

查看:77
本文介绍了淘汰赛订阅阻止了该页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可观察的数组,该数组带有一个计算得出的单选列表的当前选定项.

self.KOVehicle.SelectedPolicy = ko.computed(function () {
        return ko.utils.arrayFirst(self.KOVehicle.VehicleDetailsList(), function (veh) { return veh.PolicyId() == self.KOVehicle.SelectedPolicyId(); });
    }, self);

我正在订阅更改,该更改将更新页面上的十几个各种绑定项.

self.KOVehicle.SelectedPolicy.subscribe(function (selectedPolicy) {
        // show ajax loading spinner and do changes
    });

现在一切正常,除了似乎在进行更改时阻止了页面.除非打开js调试器并逐步执行,否则我看不到ajax加载微调器.有什么想法吗?

解决方案

我认为我也遇到了同样的问题.在我当前的KO应用程序中,用户可以单击对项目的编辑.我加载了与该项目关联的属性列表.这既触发了一些ko.computed函数,又触发了带有大量嵌套foreach语句的大型KO模板. KO会同步评估所有这些内容,这意味着所有这些都必须在JavaScript解释器返回到我的代码的下一行之前完成.在我效率低下的foreach循环中,对DOM的操作花费了2-3秒的时间,这就像您所描述的那样,使页面阻塞"了大约2-3秒.

问题很简单,这是HTML和JS的组合:

<button data-bind="click: edit">Edit</button>

// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();

edit: function(data, event) {
    // Update the list
    list(data.list)
    // Now wait 2-3 seconds for anything to happen.
}

因此,为了给用户一些即时反馈,我尝试添加一个加载微调器,这很宽松:

<button data-bind="click: edit">Edit</button>

// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();

edit: function(data, event) {
    // Update the list
    list(data.list)
    $(".spinner").show() // Doesn't appear.
    // Now wait 2-3 seconds for anything to happen. The spinner only then appears.
}

好的,所以很明显我认为我可以通过换行来解决它:

<button data-bind="click: edit">Edit</button>

// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();

edit: function(data, event) {
    $("#spinner").show() // Still doesn't appear
    // Update the list
    list(data.list) // Blocks page
    // Now wait 2-3 seconds for anything to happen.
}

这仍然不起作用-微调器直到渲染完成后才会显示.这是一个jsfiddle演示了这个问题.您可以看到页面阻止"的效果,因为按钮一直保持冻结状态,直到呈现列表为止.而且微调器直到KO完成后才出现,即使我尝试使其首先出现:

http://jsfiddle.net/6f6Rt/3/

我的原始答案建议使用超时作为解决此问题的办法,但我仍然找不到更好的方法.这是一个小提琴,它显示了超时并且还使用了计算出来的:

http://jsfiddle.net/222CG/2/

这是超时黑客:

// Show the spinner immediately...
$("#spinner").show();

// ... by using a timeout wrapped around the thing that causes the delay.
window.setTimeout(function() {
    ko.applyBindings(vm)  
}, 1)

在这个小提琴中,如果您放置

console.log("computing")

在计算出的内容中,您将看到它被计算了3001次,最初是一次,对于我们推入vm.items()的每个项目一次.在阅读有关提高应用程序性能的文章时,我遇到了麻烦:

http://knockoutjs.com/documentation/throttle-extender.html

这是一个小提琴,显示了计算所得的节气门;还带有控制台输出;现在您可以看到它只被调用了两次,这一定值得一个微不足道的性能提升!

http://jsfiddle.net/M4CUq/1/

就我的代码而言,它已部署到平板电脑上,甚至在那里运行得更慢,尤其是在CPU速度较慢的旧版Android上,因此我得出结论,我确实应该对其进行优化.我嵌套了for-each循环,都在列表上工作,所以本质上我遇到了O(n ^ 2)问题.我重写了列表数据结构,以便可以通过一个foreach循环实现我的目标.我将2-3秒的延迟降低到不到半秒.

因此,结论:

  • 我怀疑您在根本上做错了什么?但是使用Knockout可以触发一组可持续数秒的同步操作,尤其是在包含DOM操作的情况下.可以在上面的小提琴中看到这一点,这些小提琴使用了一个简单的模板,但是使用了一个很大的循环来阻塞页面3秒钟左右,

  • 您能做的最好的事情就是尝试优化渲染代码并限制一些计算量

  • 并且为了帮助使用UX,您可以使用超时破解来确保微调器在触发长时间的KO操作之前可见

哦,最后...完成所有操作后,如何隐藏微调器?我决定使用模板绑定的afterRender回调,这样就可以不使用模板本身,因此,这是最后的小提琴,表明它正在运行!

http://jsfiddle.net/A5tZU/


略有不同的方法1:持续阅读-

此小提琴显示了一种使用超时将各项逐一推送到列表中的技术.通过在推送操作之间放置超时,DOM会逐项更新.因此,总体渲染时间仍然很大,但是用户会立即得到反馈:

http://jsfiddle.net/rosenfeld/7TwcV/1/


略有不同的方法2:另一个问题-

我刚刚回答了另一个问题,我尝试使用innerHTML技巧来大大提高呈现大列表的性能.

使用基因剔除绑定约5000条记录

I have an observable array with a computed to get the currently selected item of a radio list.

self.KOVehicle.SelectedPolicy = ko.computed(function () {
        return ko.utils.arrayFirst(self.KOVehicle.VehicleDetailsList(), function (veh) { return veh.PolicyId() == self.KOVehicle.SelectedPolicyId(); });
    }, self);

I'm subscribing to changes on that which will update a dozen or so various bound items on the page.

self.KOVehicle.SelectedPolicy.subscribe(function (selectedPolicy) {
        // show ajax loading spinner and do changes
    });

Now it all works, except it seems to block the page while it's doing the changes. I don't see the ajax loading spinner unless I open the js debugger and step through it. Any ideas?

解决方案

I think I have seen the same problem. In my current KO application, the user can click edit on an item. I load a list of attributes associated with that item. This triggers both some ko.computed functions, and a large KO template with a lot of nested foreach statements. KO evaluates all this synchronously, which means it all has to complete before the JavaScript interpreter returns to the next line of my code. The DOM manipulation in my inefficient foreach loops took 2-3 seconds, which created the appearance that the page "blocks" for about 2-3 seconds, as you describe.

Here's the problem, very briefly, as a mix of HTML and JS:

<button data-bind="click: edit">Edit</button>

// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();

edit: function(data, event) {
    // Update the list
    list(data.list)
    // Now wait 2-3 seconds for anything to happen.
}

So to give the user some immediate feedback, I tried adding a loading spinner, loosely this:

<button data-bind="click: edit">Edit</button>

// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();

edit: function(data, event) {
    // Update the list
    list(data.list)
    $(".spinner").show() // Doesn't appear.
    // Now wait 2-3 seconds for anything to happen. The spinner only then appears.
}

OK, so obviously I thought I could fix it by swapping the lines:

<button data-bind="click: edit">Edit</button>

// This is my list of attributes. A lot of computeds and a big template depend on
// this observable.
var list = ko.observableArray();

edit: function(data, event) {
    $("#spinner").show() // Still doesn't appear
    // Update the list
    list(data.list) // Blocks page
    // Now wait 2-3 seconds for anything to happen.
}

This still doesn't work - the spinner doesn't appear until after the render completes. Here's a jsfiddle demonstrating exactly this problem. You can see the effect of the page "block" because the button remains frozen in its active state until the list has rendered. And the spinner doesn;t appear until after KO completes, even though I try to make it appear first:

http://jsfiddle.net/6f6Rt/3/

My original answer suggested using a timeout as a hack to workaround this, and I still cannot find a better way. Here is a fiddle showing a timeout and also using a computed:

http://jsfiddle.net/222CG/2/

Here is the timeout hack:

// Show the spinner immediately...
$("#spinner").show();

// ... by using a timeout wrapped around the thing that causes the delay.
window.setTimeout(function() {
    ko.applyBindings(vm)  
}, 1)

In this fiddle, if you put a

console.log("computing")

inside the computed, you'll see it is computed 3001 times, once initially, and once for every item we push into vm.items(). While reading about improving the performance of my app, I came across throttle:

http://knockoutjs.com/documentation/throttle-extender.html

Here's a fiddle showing throttle on the computed; also with a console output; now you can see it is only called twice, which must be worth a marginal performance gain!

http://jsfiddle.net/M4CUq/1/

With respect to my code, it is deployed to a tablet and it is even slower there, especially on old Androids with slow CPUs, so I concluded that I really should optimise it. I had nested for-each loops, both working on list, so essentially I had an O(n^2) problem. I rewrote the list data structure so that I could achieve my goal with a single foreach loop. I got the 2-3second delay down to less than half a second.

So in conclusion:

  • I doubt you're doing anything fundamentally wrong; but it is possible with Knockout to trigger a synchronous set of operations that can last for several seconds, especially if it includes DOM manipulation. This can be seen in the fiddles above, which use a simple template, but a big loop, to block the page for ~3 seconds,

  • The best thing you can do is to try to optimise your rendering code and throttle some computeds

  • and to help with UX, you can use the timeout hack to ensure a spinner is visible before you trigger the long KO operation

oh, and finally... how do you hide the spinner when everything is done? I settled on using the afterRender callback of a template binding, which you can do without a template itself, as such, so here is a final fiddle showing that in operation!

http://jsfiddle.net/A5tZU/


Slightly different approach 1: ongoing reading -

This fiddle shows a technique for using a timeout to push each item into the list one-by-one. By putting the timeout between the push operations, the DOM updates item-by-item. So the overall render time is still quite large, but the user gets immediate feedback:

http://jsfiddle.net/rosenfeld/7TwcV/1/


Slightly different approach 2: another question -

I've just answered another Q, where I try an innerHTML trick to greatly improve the performance of rendering a large list.

Binding around 5000 records using knockout

这篇关于淘汰赛订阅阻止了该页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆