dcp-client
Version:
Core libraries for accessing DCP network
175 lines (154 loc) • 5.32 kB
JavaScript
/**
* @file timer-classes.js
* This file creates classes that will be required for timing.
*
* The 2 classes defined are:
* - TimeInterval: measure an interval of time.
* - TimeThing: generic collection of TimeIntervals
*
* TimeInterval is an object shaped like {start: X, stop: Y} with a few additional functions to stop the interval.
* Start is set to the current time immediately when the interval is created, and stop is set when the `stop` function
* is called. The semantics of a duration is half open, in [begin, end) fashion, this makes calculating duration trivial,
* it's simply end - start.
*
* @author Ryan Saweczko <ryansaweczko@kingsds.network>
* @date Aug 2022
*/
self.wrapScriptLoading({ scriptName: 'timer-classes' }, function timerClasses$$fn(protectedStorage)
{
/**
* A TimeInterval is basically a [half, open) range of time that we can use to record how long
* something took.
*
* @typedef {Object} TimeInterval
* @property {number} start - the start time of the interval
* @property {number} end - the end time of the interval
* @property {function} stop - stop the interval, setting the end time to the current time
* @property {function} hasEnded - check if the interval has been stopped
* @property {function} overrideInterval - override the interval with a new start and end time
* @property {number} length - the length of the interval. This is a getter, and will throw an error
* /
/**
* Time interval class.
*
* Contains a start time and end time. The start time is set immediately when the interval is created,
* and the end time is set when `stop` is called. Once the interval has been stopped, the length
* can be accessed (getting interval.length before it's stopped is an error).
*
* @constructor {TimeInterval}
*/
function TimeInterval()
{
this.start = performance.now();
this.end = null;
}
Object.defineProperty(TimeInterval.prototype, 'length', {
get: function length()
{
// it's normal that some timers will not be all stopped when we want to measure, such as any off thread tasks
// that isn't needed due to early return
if (!this.end)
this.end = performance.now();
return this.end - this.start;
}
});
/**
* Stop a timer. The `end` time is set.
*
* @function {TimeInterval.stop}
*/
TimeInterval.prototype.stop = function stop()
{
if (this.end)
return this;
this.end = performance.now();
return this;
}
/**
* Override the interval with a new start and end time.
* @function {TimeInterval.overrideInterval}
* @param {number} start - the new start time
* @param {number} end - the new end time
*/
TimeInterval.prototype.overrideInterval = function overrideInterval(start, end)
{
this.start = start;
this.end = end;
}
/**
* Check if the interval has been stopped (end time has been set)
* @function {TimeInterval.hasEnded}
*/
TimeInterval.prototype.hasEnded = function hasEnded()
{
return typeof this.end === 'number';
};
protectedStorage.TimeInterval = TimeInterval;
/**
* @typedef {Object} TimeThing
* @property {TimeInterval[]} intervals - list of intervals
* @property {function} duration - get the total duration of all intervals
* @property {function} push - add a new interval to the list
* @property {function} reset - clear the list of intervals
*/
/**
* Time Thing class
*
* Generic collection of time intervals. Contains a list
* of intervals, and provides a way to get the total time
* of all intervals.
*
* @constructor {TimeThing}
*/
function TimeThing()
{
this.intervals = [];
}
/**
* Get the total length of all intervals. Overlapping intervals will be merged
*
* @function {TimeThing.duration}
*/
TimeThing.prototype.duration = function totalDuration()
{
// make sure intervals are sorted before merging them
this.intervals.sort((a, b) => a.start - b.start);
// solution stolen from: https://leetcode.com/problems/merge-intervals/editorial/
const merged = [];
for (const interval of this.intervals)
{
if (merged.length === 0 || merged.at(-1).end < interval.start)
// if the last interval in the merged list have no ovverlap with the current interval, just push it
merged.push(interval);
else
// otherwise, there is overlap, so merge the current and last interval
merged.at(-1).end = Math.max(merged.at(-1).end, interval.end);
}
this.intervals = merged;
// now calculating the total duration is trivial
const totalTime = merged
.map(interval => interval.length)
.reduce((a, b) => a + b, 0);
return totalTime;
}
/**
* Add a new interval.
*
* @function {TimeThing.push}
* @param {TimeInterval} interval - new interval to add to the collection
*/
TimeThing.prototype.push = function push(interval)
{
this.intervals.push(interval);
}
/**
* Reset the interval. Resets the interval list to an empty list.
*
* @function {TimeThing.reset}
*/
TimeThing.prototype.reset = function reset()
{
this.intervals = [];
}
protectedStorage.TimeThing = TimeThing;
});