index.js

const ms = require('ms');

/**
 * @class Horloge
 * @constructor Constructor
 * @example
 * ```
 * const Horloge = require('horloge');
 * // Using an async function so we can use await
 * (async () => {
 * 	const delay = duration =>
 * 		new Promise(resolve => setTimeout(resolve, duration));
 *
 * 	let t = new Horloge();
 * 	t.start();
 * 	await delay(1000);
 * 	t.stop();
 *
 * 	t.log(); // Timer took ~1000 ms
 * })();
 * ```
 */
class Horloge {
	/**
   * @constructor
   * @param options {Object} Options object
   * @note options.ms may contain options for the `ms` library
   */
	constructor(options) {
		this.options = options;
		if (!this.options.ms) {
			this.options.me = {};
		}
		this.reset();
	}

	/**
   * Initialises start and end times to null
   * @method reset
   *
   */
	reset() {
		this.startTime = null;
		this.endTime = null;
	}

	/**
   * Records the start time
   * @method start
   */
	start() {
		this.startTime = Date.now();
	}

	/**
   * Records the end time and sets the difference
   * @method stop
   */
	stop() {
		this.endTime = Date.now();
		this.difference = this.endTime - this.startTime;
	}

	/**
   * Override for the toString method
   * @method toString
   */
	toString() {
		let difference;
		if (this.options && this.options.ms) {
			difference = this.toMs(this.difference);
		} else {
			difference = `${this.difference} ms`;
		}
		return `Timer took ${difference}`;
	}

	/**
   * Convert time in milliseconds to a readable format, with provided options
   * @method toMs
   * @returns {String} Readable time with unit
   */
	toMs(val) {
		return ms(val, this.options.ms);
	}

  /**
   * Log the Horloge instance
   * @method log
   */
	log() {
		console.log(this.toString());
	}

  /**
   * Convenience function to stop the timer and log the result
   * @method stopAndLog
   */
	stopAndLog() {
		this.stop();
		this.log();
	}

  /**
   * Wraps a function with a callback and calls it, recording start and end time
   * @method wrapFunction
   * @param fn {Function} The function to wrap
   * @param callback  {Function} Callback to call when `fn` finishes
   * @returns {Horloge} An Horloge instance
   */
	static wrapFunction(fn, callback) {
		const h = new Horloge(this.options);
		h.start();
		fn(() => {
			h.stop();
			callback(null, h);
		});
	}

  /**
   * Wraps a promise and calls it, recording start and end time
   * @method wrapPromise
   * @param p {Promise} The promise to wrap
   * @returns {Horloge} An Horloge instance
   */
	static async wrapPromise(p) {
		const h = new Horloge(this.options);
		h.start();
		await p();
		h.stop();
		return h;
	}

  /**
   * Returns the raw values for start, end and (optionally) the difference between the two
   * @method raw
   * @returns {Object} raw
   */
	raw() {
		const raw = {
			start: this.startTime,
			end: this.endTime
		};

		if (this.difference) {
			Object.assign(raw, {difference: this.difference});
		}

		return raw;
	}
}

module.exports = Horloge;