Automatic clearing of setInterval and setTimeout at Playground Relaunch

Hello :slight_smile:


(I was not sure about either it should go in Bugs or Feature requests because, somehow everything is working as intended)


There is something wrong with the Playground which is that setInterval and setTimeout are never cleared. As soon as you use them, running again the script is creating a huge mess because intervals and timeouts from previous sessions are still alive.

Here is an example :
This Playground just logs "B" every second. Edit to "A" and click Run again, and you have now concurrent “A” and “B” being logged…

The problem is exactly the same with a setTimeout : set a timeout for 10 seconds later, relaunch without timeout, and boom you still trigger the old timeout from the previous session


Now, that’s quite easy to fix, I’ve fixed it on my side and I now work with this version of the playground :
There is a single file to edit : packages/tools/playground/src/tools/utilities.ts in which the code from the user is executed. I added on top of user code these lines :

if (!window.allIntervals) {
    window.allIntervals = [];
    window.allTimeouts = [];
    const setInterval_ = setInterval;
    const setTimeout_ = setTimeout;
    setInterval = function(callback, ms) {
        // Retreive additionnal args
        const args = Array.prototype.slice.call(arguments, 2);
        // Call original with args
        const id = setInterval_(callback, ms, ...args);
        // Store and return
        window.allIntervals.push(id);
        return id;
    }
    setTimeout = function(callback, ms) {
        // Retreive additionnal args
        const args = Array.prototype.slice.call(arguments, 2);
        // Call original with args
        const id = setTimeout_(callback, ms, ...args);
        // Store and return
        window.allTimeouts.push(id);
        return id;
    }
}
window.allIntervals.forEach(function(id) {
    clearInterval(id);
});
window.allTimeouts.forEach(function(id) {
    clearTimeout(id);
});
window.allIntervals = [];
window.allTimeouts = [];

The idea is quite simple :
I’m overidding the global setInterval and setTimeout functions, to store any ID returned. Then, when you hit click again (N+1 execution of the script) it calls the official clearInterval and clearTimeout on each ID. (No effect if already cleared by user)

Since the overrided functions still return the ID as expected, it works for the normal usage. As well internaly I saw that there is a setInterval triggered when we change the cursor position to restart the blinking of the cursor… It’s not affected in any way by this feature.


What do you think ? :slight_smile:

++
Tricotou

We are trying not to manipulate any native APIs of the environment, so I will not be very happy adding this code as part of the playground. What we do guarantee is that babylon observables are cleared. So instead of using setInternal (or setTimeout) you could use the timer function (or the AdvancedTimer class), similar to this:

Babylon.js Playground (babylonjs.com)

I know it might be cumbersome, but if you get used to either clear resources in scene.onDisposedObservable or use babylon-only resources and functions, you are good to go.
Same goes to event registration. We could mock addEventListener, but that’s (IMO and IMO only :slight_smile: ) should not be easily executed.

Fair enough :slight_smile: I agree tweeking Native API could be reserved to the user


Thanks for the playground. I see.
Is there a simple equivalent of setInterval as well ?

Good question…

Not really, to be honest. Out “interval” is a frame. If I want something to run every 10 frames, I use !(x % 10), but this is not time based.
You can use the timer class/function with a very large timeout and add an onTick function to check for the time passed, but I agree it is not the most convenient way of doing it.

you can use setInterval, as long as you always add a scene.onDisposeObservable observer to remove it. This is guaranteed not (!) to run inside the render loop, but for most use-cases it is more than enough.