问题
I have an observable that's binded to some input, and sometimes it's value changes too fast, so that the end user does not have time to read it. So I want to restrict the speed of changes in input.
But it's not a throttle, because throttle is a bottle neck, and the throttled observable does not change at all while it changes. I want to have a custom throttle, so that the first change applies immediately, and then it might change only after a delay (and of course, each time after a delay it shows the CURRENT value).
So far I've written the custom restrictSpeedChange extender. Here it is: http://jsfiddle.net/kasheftin/Pn9r8/4/. And it actually works for usual observables.
ko.extenders.restrictChangeSpeed = function(target,timeout) {
var writeTimeoutInstance = null;
var currentValue = target();
var updateValueAgain = false;
return ko.dependentObservable({
read: target,
write: function(value) {
var updateValue = function(value) {
target(value);
if (!writeTimeoutInstance) {
writeTimeoutInstance = setTimeout(function() {
writeTimeoutInstance = null;
if (updateValueAgain) {
updateValueAgain = false;
updateValue(currentValue);
}
},timeout);
}
}
currentValue = value;
if (!writeTimeoutInstance)
updateValue(currentValue);
else
updateValueAgain = true;
}
});
}
The problem is I want it to work with computed observables as well. For them throttle extender has throttleEvaluation variable, this variable is used in dependentObservable.js evaluatePossiblyAsync method. But I don't want to change anything in core knockout files.
In my example http://jsfiddle.net/kasheftin/Pn9r8/4/ usual observable variable is restrictChangeSpeedVar1 and it works as expected. Computed variable is restrictChangeSpeedComputedVar1. What should I do to make it work like the first one?
回答1:
A quick thought going with your existing code:
In your extender, you could see if you are dealing with a non-writeable computed observable, then return an observable that has gone through your extender and has subscribed to the computed.
ko.extenders.restrictChangeSpeed = function(target, timeout) {
var writeTimeoutInstance = null;
var currentValue = target();
var updateValueAgain = false;
var interceptor;
if (ko.isComputed(target) && !ko.isWriteableObservable(target)) {
interceptor = ko.observable().extend({ restrictChangeSpeed: timeout });
target.subscribe(interceptor);
return interceptor;
}
....
The key is that you need something to go through your "write" logic. With a normal computed, it would just re-evaluate its "read" logic and update, never going through your "write" code.
Here is a sample: http://jsfiddle.net/rniemeyer/GPbrR/
Also, you had a typo in your fiddle:
self.restrictChangeSpeedComputedVar1 = ko.computed(this.var1).extend({restictChangeSpeed:1000});
You had misspelled your extender name (restict
instead of restrict
), which made me scratch my head while testing the changes that I added, until I noticed it.
The only interesting thing about my changes is that now someone could potentially write to your computed, but it would always update whenever the underlying computed changed and I don't see why you would intentionally try to write to it.
来源:https://stackoverflow.com/questions/13207677/custom-throttle-extender-in-knockout-js