jasonray/statman-stopwatch

View on GitHub
lib/Stopwatch.js

Summary

Maintainability
A
1 hr
Test Coverage
const now = require("performance-now");
const { v4: uuid } = require("uuid");

const isBoolean = (val) => "boolean" === typeof val;

const STATES = {
    INIT: "init",
    RUNNING: "running",
    STOPPED: "stopped",
    SPLIT: "split"
};



function Stopwatch(name, autostart, delta) {
    const self = this;

    if (isBoolean(name)) {
        autostart = name;
        name = null;
    }

    if (!name) {
        name = uuid();
    }

    self._name = name;

    self.reset();
    self.setStartTimeDelta(delta);

    if (autostart) {
        this.start();
    }
}

Stopwatch.prototype.STATES = STATES;

Stopwatch.prototype.name = function () {
    const self = this;
    return self._name;
};

Stopwatch.prototype._calculateDelta = function (start, end) {
    return end - start;
};

Stopwatch.prototype.start = function () {
    const self = this;

    self._verifyState([STATES.STOPPED, STATES.INIT], "Cannot start a stopwatch that is currently running");

    self.reset();
    self._state = STATES.RUNNING;
    self.startTime = now();
};

Stopwatch.prototype.stop = function () {
    const self = this;
    self.stopTime = now();
    self._state = STATES.STOPPED;
    return this.read();
};

Stopwatch.prototype.restart = function () {
    const self = this;
    self.stop();
    self.start();
};

Stopwatch.prototype.suspend = function () {
    const self = this;
    self._verifyState([STATES.RUNNING], "Cannot suspend a stopwatch that is not running");
    return self.stop();
};

Stopwatch.prototype.resume = function () {
    const self = this;

    self._verifyState([STATES.STOPPED], "Cannot resume a stopwatch that is not running");

    const pausedTime = self.read();
    self.setStartTimeDelta(pausedTime);
    self.start();
};


Stopwatch.prototype.setStartTimeDelta = function (startTimeDelta) {
    const self = this;

    self._verifyState([STATES.STOPPED, STATES.INIT], "Cannot set an initial start time delta on a stopwatch that is currently running");

    self._StartTimeDelta = startTimeDelta;
};

Stopwatch.prototype.split = function () {
    const self = this;

    self._verifyState([STATES.RUNNING], "Cannot split time on a stopwatch that is not currently running");

    self.stopTime = now();
    self._state = STATES.SPLIT;
    return this.read();
};

Stopwatch.prototype.unsplit = function () {
    const self = this;

    self._verifyState([STATES.SPLIT], "Cannot unsplit time on a stopwatch that is not currently split");

    self.stopTime = null;
    self._state = STATES.RUNNING;
    return this.read();
};

Stopwatch.prototype.state = function () {
    return this._state;
};

Stopwatch.prototype.reset = function () {
    const self = this;
    self._state = STATES.INIT;
    self.startTime = null;
    self.stopTime = null;
};

Stopwatch.prototype.splitTime = function () {
    const self = this;

    self._verifyState([STATES.SPLIT], "Cannot get split time on a stopwatch that is not currently split");

    return self._calculateDelta(self.startTime, self.stopTime);
};

Stopwatch.prototype.read = Stopwatch.prototype.time = function (precision, units) {
    const self = this;
    const startTime = self.startTime;
    let delta = null;

    if (startTime) {
        let nowTime;
        if (self.stopTime) {
            nowTime = self.stopTime;
        } else {
            nowTime = now();
        }

        delta = self._calculateDelta(startTime, nowTime);

        if (self._StartTimeDelta) {
            delta = delta + self._StartTimeDelta;
        }

        // is in `ms` by default
        if (units === "s") {
            delta = delta / 1000;
        }

        if (precision || precision === 0) {
            delta = delta.toFixed(precision);
        }
    }

    return delta;
};

Stopwatch.prototype.toString = function () {
    const self = this;
    let name = self.name();
    let state = self.state();
    let value = self.read();
    if (value) { value = value.toFixed(2); }
    let output = `[${name} => state:${state}; value:${value}]`;
    return output;
};

Stopwatch.prototype.prettyPrint = function () {
    process.stdout.write(this.toString() + "\n");
};

Stopwatch.prototype._verifyState = function (allowedStates, failedMessage) {
    const self = this;

    if (!allowedStates.includes(self.state())) {
        if (!failedMessage) {
            failedMessage = "Unexpected state";
        }
        throw new Error(failedMessage + " (" + self.state() + ")");
    }
};

module.exports = Stopwatch;