Knockout订阅阻止了该页面

时间:2014-07-21 00:25:09

标签: javascript knockout.js

我有一个带有计算的可观察数组,用于获取当前所选的无线电列表项。

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
    });

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

1 个答案:

答案 0 :(得分:1)

我想我也遇到过同样的问题。在我当前的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速度慢的旧Androids上,所以我总结说我真的应该优化它。我已经嵌套了每个循环,都在列表上工作,所以基本上我有一个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技巧来大大提高渲染大型列表的性能。

Binding around 5000 records using knockout