timing/Loop.js

class Loop {
    /**
     * Basic game loop, cb is called tps times (ticks per second)
     * 
     * > **Beware** This is just a basic `setTimeout` based loop. Due to how `setTimeout` is implemented in node.js this can be very inaccurate. If you need a more precise timing, use [PreciseLoop](#PreciseLoop)
     * @param {Function} cb 
     * @param {Number} tps 
     * @param {Boolean} autostart 
     */
    constructor(cb, tps, autostart = true) {
        this.cb = cb;

        this.tps = tps;
        this.target_ms = Math.floor(1000 / tps);

        /**
         * ms timestamp of the last time
         */
        this.lasttick = null;

        this.running = false;

        if (autostart) this.start();
    }

    /**
     * start the loop
     */
    start() {
        if (!this.running) {
            this.tick();
            this.running = true;
        }
    }

    /**
     * stop the loop
     */
    stop() {
        if (this.running && this.to) {
            clearTimeout(this.to);
            this.running = false;
        }
    }

    tick() {
        let pre = Date.now();
        let dt = this.lasttick !== null ? pre - this.lasttick : 0;

        this.cb(dt);

        let post = Date.now();
        let elapsed = post - pre;
        let wait = this.target_ms - elapsed;

        this.to = setTimeout(() => {
            this.tick();
        }, wait);

        this.lasttick = post;
    }
}

export default Loop;