windowjs / windowjs

Window.js is an open-source Javascript runtime for desktop graphics programming.

Home Page:https://windowjs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add setInterval and clearInterval

ArjixWasTaken opened this issue · comments

I noticed that setInterval is not a thing in windowjs, so I decided to write my own implementation:

function* getIntervalIdgen() {
    var i = 0;
    yield i++;
}

const intervalIdGenerator = getIntervalIdgen();

function getId() {
    return intervalIdGenerator.next.value;
}

{
    var g;
    if (typeof window !== "undefined") {
        g = window;
    } else if (typeof global !== "undefined") {
        g = global;
    } else if (typeof self !== "undefined") {
        g = self;
    } else {
        g = this;
    }

    g.setInterval = function (callback, timer) {
        let id = getId();

        g.intervalState.intervals.push({
            callback,
            timer,
            timeDelta: Date.now(),
            id,
        });

        return id;
    };
    g.clearInterval = function (id) {
        g.intervalState.intervals = g.intervalState.intervals.filter(function (
            item
        ) {
            return item.id != id;
        });
    };
    g.intervalState = {};
    g.intervalState.intervals = [];

    function fireIntervals() {
        requestAnimationFrame(fireIntervals);

        for (const interval of g.intervalState.intervals) {
            if (Date.now() - interval.timeDelta >= interval.timer) {
                interval.callback();
                interval.timeDelta = Date.now();
            }
        }
    }
    requestAnimationFrame(fireIntervals);
}

Would be nice if I could contribute that to the repo, but I don't want to mess with c.

Hi Arjix, thanks for the contribution!

Agreed that Window.js should also have setInterval and clearInterval.

Window.js doesn't preload a Javascript file, other than the native builtins, so this approach doesn't work right now. I don't think we want to add a preloaded Javascript file because it will take a bit longer to load compared to native code, so it would make every startup a bit slower than it could be.

Some comments about the code you wrote:

  1. The global object in Window.js is globalThis. See these docs:

https://windowjs.org/doc/global
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis

So you should just do

globalThis.setInterval = function(callback, interval) { ... }

  1. Date.now() is not a stable time source; for example, it may go back in time during daylight savings days (two times a year), or if the user adjusts the clock, or for any other reason.

Clocks for intervals like in setInterval should use a monotonic time source, that only advances forward and always at a constant rate. A good source for that in Javascript is performance.now():

https://developer.mozilla.org/en-US/docs/Web/API/Performance/now

So you could just take a timestamp when a callback is registered, and then compare its timestamp to performance.now() when firing callbacks.

  1. setInterval() may be called from inside a callback! So the for loop in fireIntervals() may end up mutating g.intervalState.intervals while it's still looping (same for clearInterval). This may cause strange bugs like skipping ahead when a callback gets removed.

--

I guess this change will really have to go in the C++ code. Here's a pointer to the table of setTimeout callbacks if you want to give this a try:

std::unordered_map<uint32_t, v8::Global<v8::Function>> timeouts_;

See also how it's implemented:

api->task_queue()->Post(timeout, [=] {

The task_queue is used internally to run Javascript callbacks once on every frame. I think that's the ideal rate to call setInterval: each callback gets called at most once per loop, and as fast as frames are getting pushed to the screen.

Let me know if you'd like to make that change, or if someone else should implement this.

I am not an experienced c++ dev, so I bet if I go ahead and do it myself I will introduce a bug or smth.
And that would place more work on you in order to troubleshoot it.

So yeah, someone else should implement this.

Sure, np. You can always have your own module with globals and import it manually in your programs:

import './path/to/stuff.js';

Just put anything you'd like in the globalThis object.

ezoic increase your site revenue