简体   繁体   中英

How to call function only once when multiple subscribed variables are called together

This is not specifically a technical question but I was curious what is the best approach to such a problem? Although I have this issue in Knockout, I'm sure the use case will be valid elsewhere too.

Lets say I have subscribed 2 variables, simpleObserve1 , simpleObserve2 so that every time their value changes, they call a function resetAllValues() .

var simpleObserve1 = ko.observable(0), // initial values
    simpleObserve2 = ko.observable(0); // initial values

var resetAllValues = function resetAllValues() {
    /* this function takes all observable values and resets them */
    {...}
}

simpleObserve1.subscribe(function(){
    resetAllValues();
});

simpleObserve2.subscribe(function(){
    resetAllValues();
});

simpleObserve1(5); // value changed anywhere in code
simpleObserve2(10); // value changed anywhere in code

2 questions here.

  1. When resetAllValues() is called, it changes all subscribed values to 0, including simpleObserve1 and simpleObserve2 . This in turn calls resetAllValues() over and over again. How do I prevent this from going into an infinite loop?
  2. What if I want to update both the variables together, but call the resetAllValues() only once?

I've tried to use the knockout's dispose() method to help me along, but I was wondering if there is a better way to do this.

Deferred Updates might help you out. By using observables' values in a computed, knockout creates a subscription. By extending this computed, rapidly succeeding changes are combined in some sort of micro-task.

They prevent the looping behavior, but it's still kind of unclear how many updates are triggered. Ie: when setting to 5 and 10 results in 1 or 2 computed updates. So I'm not entirely sure if this answers your question.

 var i = 0, simpleObserve1 = ko.observable(0), // initial values simpleObserve2 = ko.observable(0); // initial values ko.computed(function resetAllValues() { console.log("Set " + ++i + ", before:"); console.log("1: ", simpleObserve1()); console.log("2: ", simpleObserve2()); simpleObserve1(0); simpleObserve2(0); console.log("Set " + i + ", after:"); console.log("1: ", simpleObserve1()); console.log("2: ", simpleObserve2()); }).extend({ deferred: true }); simpleObserve1(5); // value changed anywhere in code simpleObserve2(10); // value changed anywhere in code 
 .as-console-wrapper { min-height: 100%; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 

I've created a higher order function acceptXParams , that will check if the number of params is equal to fn.length or an arbitrary number. If not, the original function won't be invoked:

 function acceptXParams(fn, numOfParams) { var numOfParams = numOfParams === undefined ? fn.length : numOfParams; return function() { if(arguments.length !== numOfParams) { return; } return fn.apply(fn, arguments); } } /** example **/ function sum(a, b, c) { return a + b + c; } var sum3 = acceptXParams(sum); console.log(sum3(1, 2, 3)); console.log(sum3(1, 2)); console.log(sum3(1, 2, 3, 4)); 

And a more sleek ES6 version acceptXParams :

const acceptXParams = (fn, numOfParams = fn.length) => 
    (...args) => 
        args.length === numOfParams ? fn(...args) : undefined;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM