'Preventing an infinite loop on a setter in JS/TS

With TypeScript, I've been trying to set up what I call a "trigger" : an object that contains a checker function (returns a Boolean) and a behavior function, which can do basically anything. What I want to do is every time a certain variable's value is changed, the checker function of the trigger is executed, and if it returns true, the behavior function is ran too.

Naturally, I put the trigger's checker function call in a setter :

set health(value: number) {
    this._health = value;
    runTriggers();
}

The goal of a trigger's behavior function is to do anything, but mainly change values from variables. And that is where the problem is : if a trigger is called in the health setter like above, and the behavior function of the trigger also changes the health variable (thus calling the setter again), it will end up in an infinite recursive call.

How could I prevent this infinite call from happening, while still allowing my trigger's behavior function to freely change variables?



Solution 1:[1]

The solution is likely to be very dependent on your overall design choices. Fundamentally, you have to have a way to say "don't do an infinite loop," which means that somewhere, sometime, something is going to have to be able to set health without running triggers.

A simple flag would let you avoid even one recursive trigger call:

set health(value: number) {
    this._health = value;
    if (!this._healthTriggerActive) {
        this._healthTriggerActive = true;
        try {
            runTriggers();
        } finally {
            this._healthTriggerActive = false;
        }
    }
}

...but that of course means that setting health in a trigger callback won't run triggers (since that's the basic problem).

You could also limit depth with a counter, allowing a certain amount of recursion but not too much; but that's probably just hiding the problem.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 T.J. Crowder