Sending periodic heartbeats, wondering if there are any timer bugs What?

heartbeatjs is a small light weight library that helps you run periodic heartbeat functions and detects timeouts when they occur by launching events.

It was mainly designed for tcp/ip connections, but you can use it with any protocol you want as it designed to be generic.

Project

The project page, with examples and documentation can be  seen here:

https://fl4m3ph03n1x.github.io/heartbeatjs/index.html

Code

This library is mostly a collection of getters and setters mixed with some timers. I would like a second opinion regarding the code and any possible bugs one may find, as I am about to release the next version next week.

const isFunction = require("lodash.isfunction");


const DEFAULT_TIMEOUT = 5000;
const DEFAULT_INTERVAL = 3000;

const heartBeatFactory = () =&gt; {

    let interval = DEFAULT_INTERVAL,
        timeout = DEFAULT_TIMEOUT,
        ping,
        pong,
        timer,
        lastHeartbeatTime,
        timeoutTimer,
        hasStarted = false;

    const events = {
        timeout: () =&gt; {}
    };

    const hasTimedOut = () =&gt;
        Date.now() - lastHeartbeatTime &gt; timeout;

    const getBeatInterval = () =&gt; interval;

    const setBeatInterval = newInterval =&gt; {
        if(isNaN(newInterval))
            throw new TypeError(`${newInterval} must be a Number.`);

        interval = newInterval;
    };

    const getBeatTimeout = () =&gt; timeout;

    const setBeatTimeout = newTimeout =&gt; {
        if(isNaN(newTimeout))
            throw new TypeError(`${newTimeout} must be a Number.`);

        timeout = newTimeout;
        clearTimeout(timeoutTimer);
        timeoutTimer = setTimeout(events.timeout, getBeatTimeout());
    };

    const getPing = () =&gt; ping;

    const setPing = newPing =&gt; {
        ping = newPing;
    };

    const getPong = () =&gt; pong;

    const setPong = newPong =&gt; {
        pong = newPong;
    };

    const receivedPong = () =&gt; {
        lastHeartbeatTime = Date.now();
        clearTimeout(timeoutTimer);
        timeoutTimer = setTimeout(events.timeout, getBeatTimeout());
    };

    const stop = () =&gt; {
        lastHeartbeatTime = undefined;
        clearInterval(timer);
        timer = undefined;
        clearTimeout(timeoutTimer);
        timeoutTimer = undefined;
    };

    const start = fn =&gt; {
        if (!isFunction(fn))
            throw new TypeError(`${fn} must be a function.`);

        hasStarted = true;
        lastHeartbeatTime = Date.now();
        timer = setInterval(fn, getBeatInterval());
        timeoutTimer = setTimeout(events.timeout, getBeatTimeout());
    };

    const onTimeout = fn =&gt; {
        if (!isFunction(fn))
            throw new TypeError(`${fn} must be a function.`);

        events.timeout = fn;
    };

    const isBeating = () =&gt; timer !== undefined;

    const reset = () =&gt; {
        if (isBeating())
            stop();

        if( hasStarted ){
            setBeatInterval(DEFAULT_INTERVAL);
            setBeatTimeout(DEFAULT_TIMEOUT);
            ping = undefined;
            pong = undefined;
            onTimeout(() =&gt; {});
        }
    };

    return Object.freeze({
        getBeatInterval,
        setBeatInterval,
        getBeatTimeout,
        setBeatTimeout,
        hasTimedOut,
        getPing,
        setPing,
        getPong,
        receivedPong,
        setPong,
        stop,
        start,
        reset,
        isBeating,
        onTimeout
    });
};

module.exports = heartBeatFactory;
Just wondering why you don't set hasStarted = false either in the stop() or in the reset() method.

By doing it in the stop() you could omit the second if in the reset() method.

Seeing a reset() method at least screams for resetting all values to default.