KnockoutJS 3.X API 第七章 其他技术(4) 速率限制
注意:这个速率限制API是在Knockout 3.1.0中添加的。
通常,更改的observable立即通知其订户,以便依赖于observable的任何计算的observable或绑定都会同步更新。 但是,rateLimit扩展器会导致observable在指定的时间段内抑制和延迟更改通知。 因此,速率限制的observable异步更新依赖关系。
速率限制扩展器可以应用于任何类型的可观察量,包括可观察数组和计算可观察量。 速率限制的主要用例是:
- 使事情在一定延迟后作出反应
- 将多个更改合并到单个更新中
如果您只需要组合更新而不添加延迟,则延迟更新提供了一种更有效的方法。
应用速率限制扩展
速率限制支持两种参数格式:
// Shorthand: Specify just a timeout in milliseconds someObservableOrComputed.extend({ rateLimit: 500 }); // Longhand: Specify timeout and/or method someObservableOrComputed.extend({ rateLimit: { timeout: 500, method: "notifyWhenChangesStop" } });
方法选项控制通知何时触发,并接受以下值:
-
notifyAtFixedRate
— 未另行指定时的默认值。 通知发生在从第一次更改到可观察者的指定时间段(最初或自上一次通知之后)。 -
notifyWhenChangesStop
— 通知发生在可观察者在指定时间段内没有发生变化之后。 每次可观察到的变化,该定时器被重置,因此如果可观察者连续地改变比超时期间更频繁,则不能发生通知。
示例1:基础示例
考虑下面代码中的observable:
var name = ko.observable('Bert'); var upperCaseName = ko.computed(function() { return name().toUpperCase(); });
通常,如果您更改名称如下:
name('The New Bert');
upperCase Name将在下一行代码运行之前立即完成。 但是如果你改为使用rateLimit定义名称如下:
var name = ko.observable('Bert').extend({ rateLimit: 500 });
upperCaseName不会在名称更改时立即重新计算,而是在将其新值通知给upperCaseName之前,将等待500毫秒(半秒),然后重新计算其值。 无论在这500ms内名称是多少次更改,upperCaseName只会更新一次最新的值。
示例2:当用户停止输入时执行某些操作
在这个实例中,有一个instantValue observable,当你按下一个键时立即反应。 然后将其封装在delayedValue计算observable中,该observable配置为仅当更改停止至少400毫秒时通知使用notifyWhenChangesStop rate-limit方法。
尝试一下:
Type stuff here:
Current delayed value:
Stuff you have typed:
UI源码:
<p>Type stuff here: <input data-bind='textInput: instantaneousValue' /></p> <p>Current delayed value: <b data-bind='text: delayedValue'> </b></p> <div data-bind="visible: loggedValues().length > 0"> <h3>Stuff you have typed:</h3> <ul data-bind="foreach: loggedValues"> <li data-bind="text: $data"></li> </ul> </div>
视图模型源码:
function AppViewModel() { this.instantaneousValue = ko.observable(); this.delayedValue = ko.pureComputed(this.instantaneousValue) .extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 400 } }); // Keep a log of the throttled values this.loggedValues = ko.observableArray([]); this.delayedValue.subscribe(function (val) { if (val !== '') this.loggedValues.push(val); }, this); } ko.applyBindings(new AppViewModel());
计算可观测量的特殊考虑
对于计算的observable,当计算的observable的依赖性之一改变而不是其值改变时,触发速率限制定时器。 计算的observable不会重新求值,直到实际需要它的值 - 在发生更改通知的超时时间段之后,或直接访问计算的可观察值时。 如果需要访问计算的最近评估的值,可以使用peek方法执行此操作。
强制限速观察者总是通知订阅者
当any observable的值是原始值(数字,字符串,布尔值或null)时,只有当observable的依赖项设置为实际上与之前不同的值时,才会通知它的依赖项。 因此,原始值的速率限制可观察量只有当它们的值在超时周期结束时实际上不同时才通知。 换句话说,如果原始值的速率限制的observable被改变为新的值,然后在超时时间段结束之前改变回原始值,则不会发生通知。
如果要确保始终通知订阅者更新,即使该值相同,除了rateLimit之外,还要使用notify扩展器:
myViewModel.fullName = ko.computed(function() { return myViewModel.firstName() + " " + myViewModel.lastName(); }).extend({ notify: 'always', rateLimit: 500 });
与延迟更新的比较
Knockout 3.4.0版本增加了对延迟更新的支持,通过使通知和更新异步,它的工作方式类似于速率限制。 但是,不是使用定时延迟,而是在执行I / O,回流或重绘之后,在当前任务之后尽快处理延迟更新。 如果要升级到3.4.0并且使用速率限制超时(例如,0毫秒)的代码,则可以修改为使用延迟更新:
ko.computed(function() { // .... }).extend({ deferred: true });
与throttle延长器比较
如果要使用已弃用的throttle扩展程序迁移代码,则应注意以下方法,即rateLimit扩展程序与throttle扩展程序不同。
使用rateLimit时:
- 对可观测量的写入不被延迟; observables值立即更新。 对于可写的计算可观测量,这意味着写函数总是立即运行。
- 所有更改通知都会延迟,包括手动调用valueHasMutated时。 这意味着您不能使用valueHasMutated强制速率限制的observable通知未更改的值。
- 默认速率限制方法与throttle算法不同。 要匹配throttle行为,请使用notifyWhenChangesStop方法。
- 速率限制的计算观察值的评估不受速率限制; 如果你读它的值,它会重新评估。