Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hypertimer

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hypertimer - npm Package Compare versions

Comparing version 0.0.1 to 0.1.0

examples/browser/clock.html

6

bower.json
{
"name": "hypertimer",
"version": "0.0.1",
"description": "A timer running faster or slower than realtime, in continuous or discrete time.",
"version": "0.1.0",
"description": "A timer running faster or slower than real-time, in continuous or discrete time.",
"homepage": "https://github.com/enmasseio/hypertimer",

@@ -13,4 +13,2 @@ "repository": {

"coverage",
"lib",
"test",
".idea",

@@ -17,0 +15,0 @@ "gulpfile.js",

@@ -5,7 +5,7 @@ /**

*
* A timer running faster or slower than realtime, in continuous or discrete
* time.
* A timer running faster or slower than real-time, and in continuous or
* discrete time.
*
* @version 0.0.1
* @date 2014-07-11
* @version 0.1.0
* @date 2014-07-15
*

@@ -27,3 +27,2 @@ * @license

*/
(function webpackUniversalModuleDefinition(root, factory) {

@@ -92,13 +91,451 @@ if(typeof exports === 'object' && typeof module === 'object')

var util = __webpack_require__(2);
// enum for type of timeout
var TYPE = {
TIMEOUT: 0,
INTERVAL: 1,
TRIGGER: 2
};
/**
* Create a hypertimer
* @param {Object} options
* Create a new hypertimer
* @param {Object} [options] The following options are available:
* rate: number The rate of speed of hyper time with
* respect to real-time in milliseconds
* per millisecond. By default, rate
* is 1. Note that rate can even be a
* negative number.
*/
module.exports = function hypertimer (options) {
throw new Error('Not yet implemented, coming soon...');
};
function hypertimer(options) {
// options
var rate = 1; // number of milliseconds per milliseconds
// properties
var running = false; // true when running
var realTime = null; // timestamp. the moment in real-time when hyperTime was set
var hyperTime = null; // timestamp. the start time in hyper-time
var timeouts = []; // array with all running timeouts
var timeoutId = null; // currently running timer
var idSeq = 0; // counter for unique timeout id's
// exported timer object with public functions and variables
var timer = {};
/**
* Change configuration options of the hypertimer, or retrieve current
* configuration.
* @param {Object} [options] The following options are available:
* rate: number The rate (in milliseconds per
* millisecond) at which the timer
* runs, with respect to real-time
* speed. By default, rate is 1.
* Note that rate can even be a
* negative number.
* @return {Object} Returns the applied configuration
*/
timer.config = function(options) {
if (options) {
if ('rate' in options) {
var newRate = Number(options.rate);
if (isNaN(newRate)) {
throw new TypeError('rate must be a number');
}
// TODO: add option rate='discrete'
hyperTime = timer.now();
realTime = util.nowReal();
rate = newRate;
}
}
// reschedule running timeouts
_schedule();
// return a copy of the configuration options
return {
rate: rate
};
};
/**
* Set the time of the timer. To get the current time, use getTime() or now().
* @param {number | Date} time The time in hyper-time.
*/
timer.setTime = function (time) {
if (time instanceof Date) {
hyperTime = time.valueOf();
}
else {
var newTime = Number(time);
if (isNaN(newTime)) {
throw new TypeError('time must be a Date or number');
}
hyperTime = newTime;
}
// reschedule running timeouts
_schedule();
};
/**
* Returns the current time of the timer as a number.
* See also getTime().
* @return {number} The time
*/
timer.now = function () {
if (running) {
// TODO: implement performance.now() / process.hrtime(time) for high precision calculation of time interval
var realInterval = util.nowReal() - realTime;
var hyperInterval = realInterval * rate;
return hyperTime + hyperInterval;
}
else {
return hyperTime;
}
};
/**
* Continue the timer.
*/
timer['continue'] = function() {
realTime = util.nowReal();
running = true;
// reschedule running timeouts
_schedule();
};
/**
* Pause the timer. The timer can be continued again with `continue()`
*/
timer.pause = function() {
hyperTime = timer.now();
realTime = null;
running = false;
// reschedule running timeouts (pauses them)
_schedule();
};
/**
* Returns the current time of the timer as Date.
* See also now().
* @return {Date} The time
*/
// rename to getTime
timer.getTime = function() {
return new Date(timer.now());
};
/**
* Get the value of the hypertimer. This function returns the result of getTime().
* @return {Date} current time
*/
timer.valueOf = timer.getTime;
/**
* Return a string representation of the current hyper-time.
* @returns {string} String representation
*/
timer.toString = function () {
return timer.getTime().toString();
};
/**
* Set a timeout, which is triggered when the timeout occurs in hyper-time.
* See also setTrigger.
* @param {Function} callback Function executed when delay is exceeded.
* @param {number} delay The delay in milliseconds. When the rate is
* zero, or the delay is smaller or equal to
* zero, the callback is triggered immediately.
* @return {number} Returns a timeoutId which can be used to cancel the
* timeout using clearTimeout().
*/
timer.setTimeout = function(callback, delay) {
var id = idSeq++;
var timestamp = timer.now() + delay;
if (isNaN(timestamp)) {
throw new TypeError('delay must be a number');
}
// add a new timeout to the queue
_queueTimeout({
id: id,
type: TYPE.TIMEOUT,
time: timestamp,
callback: callback
});
// reschedule the timeouts
_schedule();
return id;
};
/**
* Set a trigger, which is triggered when the timeout occurs in hyper-time.
* See also getTimeout.
* @param {Function} callback Function executed when timeout occurs.
* @param {Date | number} time An absolute moment in time (Date) when the
* callback will be triggered. When the rate is
* zero, or the date is a Date in the past,
* the callback is triggered immediately.
* @return {number} Returns a triggerId which can be used to cancel the
* trigger using clearTrigger().
*/
timer.setTrigger = function (callback, time) {
var id = idSeq++;
var timestamp = Number(time);
if (isNaN(timestamp)) {
throw new TypeError('time must be a Date or number');
}
// add a new timeout to the queue
_queueTimeout({
id: id,
type: TYPE.TRIGGER,
time: timestamp,
callback: callback
});
// reschedule the timeouts
_schedule();
return id;
};
/**
* Trigger a callback every interval. Optionally, a start date can be provided
* to specify the first time the callback must be triggered.
* See also setTimeout and setTrigger.
* @param {Function} callback Function executed when delay is exceeded.
* @param {number} interval Interval in milliseconds. When interval
* is smaller than zero or is infinity, the
* interval will be set to zero and triggered
* with a maximum rate.
* @param {Date | number} [firstTime] An absolute moment in time (Date) when the
* callback will be triggered the first time.
* By default, firstTime = now() + interval.
* @return {number} Returns a intervalId which can be used to cancel the
* trigger using clearInterval().
*/
timer.setInterval = function(callback, interval, firstTime) {
var id = idSeq++;
var _interval = Number(interval);
if (isNaN(_interval)) {
throw new TypeError('interval must be a number');
}
if (_interval < 0 || !isFinite(_interval)) {
_interval = 0;
}
var timestamp;
if (firstTime != undefined) {
timestamp = Number(firstTime);
if (isNaN(timestamp)) {
throw new TypeError('firstTime must be a Date or number');
}
}
else {
// firstTime is undefined or null
timestamp = (timer.now() + _interval);
}
// add a new timeout to the queue
_queueTimeout({
id: id,
type: TYPE.INTERVAL,
time: timestamp,
interval: _interval,
//firstTime: timestamp,
//occurrence: 0,
callback: callback
});
// reschedule the timeouts
_schedule();
return id;
};
/**
* Cancel a timeout
* @param {number} timeoutId The id of a timeout
*/
timer.clearTimeout = function(timeoutId) {
// find the timeout in the queue
for (var i = 0; i < timeouts.length; i++) {
if (timeouts[i].id === timeoutId) {
// remove this timeout from the queue
timeouts.splice(i, 1);
// reschedule timeouts
_schedule();
break;
}
}
};
/**
* Cancel a trigger
* @param {number} triggerId The id of a trigger
*/
timer.clearTrigger = timer.clearTimeout;
timer.clearInterval = timer.clearTimeout;
/**
* Returns a list with the id's of all timeouts
* @returns {number[]} Timeout id's
*/
timer.list = function () {
return timeouts.map(function (timeout) {
return timeout.id;
});
};
/**
* Clear all timeouts
*/
timer.clear = function () {
// empty the queue
timeouts = [];
// reschedule
_schedule();
};
/**
* Add a timeout to the queue. After the queue has been changed, the queue
* must be rescheduled by executing _reschedule()
* @param {{type: number, time: number, callback: Function}} params
* @private
*/
function _queueTimeout(params) {
// insert the new timeout at the right place in the array, sorted by time
if (timeouts.length > 0) {
var i = timeouts.length - 1;
while (i >= 0 && timeouts[i].time > params.time) {
i--;
}
// insert the new timeout in the queue. Note that the timeout is
// inserted *after* existing timeouts with the exact *same* time,
// so the order in which they are executed is deterministic
timeouts.splice(i + 1, 0, params);
}
else {
// queue is empty, append the new timeout
timeouts.push(params);
}
}
/**
* Reschedule all queued timeouts
* @private
*/
function _schedule() {
var timeout;
var next = timeouts[0];
// cancel timer when running
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
if (running && next) {
// schedule next timeout
var time = next.time;
var delay = time - timer.now();
var realDelay = delay / rate;
function trigger() {
// execute all expired timeouts
var intervals = [];
while (timeouts.length > 0 &&
((timeouts[0].time <= time) || !isFinite(timeouts[0].time))) {
timeout = timeouts[0];
// execute the callback
try {
timeout.callback();
} catch (err) {
// silently ignore errors thrown by the callback
}
// in case of an interval we have to reschedule on next cycle
if (timeout.type === TYPE.INTERVAL && timeouts[0] === timeout) {
// timeout is not removed inside the callback, reschedule it
intervals.push(timeout);
}
timeouts.shift();
}
// reschedule intervals
for (var i = 0; i < intervals.length; i++) {
timeout = intervals[i];
// FIXME: adding the interval each occurrence will give round-off errors.
// however, when multliplying the firstTime with the number of occurrences,
// we cannot easily swith the rate at any time.
//timeout.occurrence++;
//timeout.time = timeout.firstTime + timeout.interval * timeout.occurrence * (rate < 0 ? -1 : 1);
timeout.time += timeout.interval * (rate < 0 ? -1 : 1);
_queueTimeout(timeout);
}
// initialize next round of timeouts
_schedule();
}
timeoutId = setTimeout(trigger, realDelay);
}
}
Object.defineProperty(timer, 'running', {
get: function () {
return running;
}
});
timer.config(options); // apply options
timer.setTime(util.nowReal()); // set time as current real time
timer.continue(); // start the timer
return timer;
}
module.exports = hypertimer;
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
/* istanbul ignore else */
if (typeof Date.now === 'function') {
/**
* Helper function to get the current time
* @return {number} Current time
*/
exports.nowReal = function () {
return Date.now();
}
}
else {
/**
* Helper function to get the current time
* @return {number} Current time
*/
exports.nowReal = function () {
return new Date().valueOf();
}
}
/***/ }
/******/ ])
})

@@ -5,7 +5,7 @@ /**

*
* A timer running faster or slower than realtime, in continuous or discrete
* time.
* A timer running faster or slower than real-time, and in continuous or
* discrete time.
*
* @version 0.0.1
* @date 2014-07-11
* @version 0.1.0
* @date 2014-07-15
*

@@ -27,3 +27,3 @@ * @license

*/
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):"object"==typeof exports?exports.hypertimer=t():e.hypertimer=t()}(this,function(){return function(e){function t(r){if(o[r])return o[r].exports;var n=o[r]={exports:{},id:r,loaded:!1};return e[r].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var o={};return t.m=e,t.c=o,t.p="",t(0)}([function(e,t,o){e.exports=o(1)},function(e){e.exports=function(){throw new Error("Not yet implemented, coming soon...")}}])});
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):"object"==typeof exports?exports.hypertimer=t():e.hypertimer=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={exports:{},id:r,loaded:!1};return e[r].call(i.exports,i,i.exports,t),i.loaded=!0,i.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){function r(e){function t(e){if(c.length>0){for(var t=c.length-1;t>=0&&c[t].time>e.time;)t--;c.splice(t+1,0,e)}else c.push(e)}function n(){function e(){for(var e=[];c.length>0&&(c[0].time<=f||!isFinite(c[0].time));){i=c[0];try{i.callback()}catch(u){}i.type===o.INTERVAL&&c[0]===i&&e.push(i),c.shift()}for(var a=0;a<e.length;a++)i=e[a],i.time+=i.interval*(0>r?-1:1),t(i);n()}var i,a=c[0];if(l&&(clearTimeout(l),l=null),u&&a){var f=a.time,m=f-s.now(),p=m/r;l=setTimeout(e,p)}}var r=1,u=!1,a=null,f=null,c=[],l=null,m=0,s={};return s.config=function(e){if(e&&"rate"in e){var t=Number(e.rate);if(isNaN(t))throw new TypeError("rate must be a number");f=s.now(),a=i.nowReal(),r=t}return n(),{rate:r}},s.setTime=function(e){if(e instanceof Date)f=e.valueOf();else{var t=Number(e);if(isNaN(t))throw new TypeError("time must be a Date or number");f=t}n()},s.now=function(){if(u){var e=i.nowReal()-a,t=e*r;return f+t}return f},s["continue"]=function(){a=i.nowReal(),u=!0,n()},s.pause=function(){f=s.now(),a=null,u=!1,n()},s.getTime=function(){return new Date(s.now())},s.valueOf=s.getTime,s.toString=function(){return s.getTime().toString()},s.setTimeout=function(e,r){var i=m++,u=s.now()+r;if(isNaN(u))throw new TypeError("delay must be a number");return t({id:i,type:o.TIMEOUT,time:u,callback:e}),n(),i},s.setTrigger=function(e,r){var i=m++,u=Number(r);if(isNaN(u))throw new TypeError("time must be a Date or number");return t({id:i,type:o.TRIGGER,time:u,callback:e}),n(),i},s.setInterval=function(e,r,i){var u=m++,a=Number(r);if(isNaN(a))throw new TypeError("interval must be a number");(0>a||!isFinite(a))&&(a=0);var f;if(void 0!=i){if(f=Number(i),isNaN(f))throw new TypeError("firstTime must be a Date or number")}else f=s.now()+a;return t({id:u,type:o.INTERVAL,time:f,interval:a,callback:e}),n(),u},s.clearTimeout=function(e){for(var t=0;t<c.length;t++)if(c[t].id===e){c.splice(t,1),n();break}},s.clearTrigger=s.clearTimeout,s.clearInterval=s.clearTimeout,s.list=function(){return c.map(function(e){return e.id})},s.clear=function(){c=[],n()},Object.defineProperty(s,"running",{get:function(){return u}}),s.config(e),s.setTime(i.nowReal()),s.continue(),s}var i=n(2),o={TIMEOUT:0,INTERVAL:1,TRIGGER:2};e.exports=r},function(e,t){t.nowReal="function"==typeof Date.now?function(){return Date.now()}:function(){return(new Date).valueOf()}}])});
//# sourceMappingURL=hypertimer.map

@@ -5,4 +5,4 @@ /**

*
* A timer running faster or slower than realtime, in continuous or discrete
* time.
* A timer running faster or slower than real-time, and in continuous or
* discrete time.
*

@@ -26,2 +26,2 @@ * @version @@version

* the License.
*/
*/

@@ -0,7 +1,419 @@

var util = require('./util');
// enum for type of timeout
var TYPE = {
TIMEOUT: 0,
INTERVAL: 1,
TRIGGER: 2
};
/**
* Create a hypertimer
* @param {Object} options
* Create a new hypertimer
* @param {Object} [options] The following options are available:
* rate: number The rate of speed of hyper time with
* respect to real-time in milliseconds
* per millisecond. By default, rate
* is 1. Note that rate can even be a
* negative number.
*/
module.exports = function hypertimer (options) {
throw new Error('Not yet implemented, coming soon...');
};
function hypertimer(options) {
// options
var rate = 1; // number of milliseconds per milliseconds
// properties
var running = false; // true when running
var realTime = null; // timestamp. the moment in real-time when hyperTime was set
var hyperTime = null; // timestamp. the start time in hyper-time
var timeouts = []; // array with all running timeouts
var timeoutId = null; // currently running timer
var idSeq = 0; // counter for unique timeout id's
// exported timer object with public functions and variables
var timer = {};
/**
* Change configuration options of the hypertimer, or retrieve current
* configuration.
* @param {Object} [options] The following options are available:
* rate: number The rate (in milliseconds per
* millisecond) at which the timer
* runs, with respect to real-time
* speed. By default, rate is 1.
* Note that rate can even be a
* negative number.
* @return {Object} Returns the applied configuration
*/
timer.config = function(options) {
if (options) {
if ('rate' in options) {
var newRate = Number(options.rate);
if (isNaN(newRate)) {
throw new TypeError('rate must be a number');
}
// TODO: add option rate='discrete'
hyperTime = timer.now();
realTime = util.nowReal();
rate = newRate;
}
}
// reschedule running timeouts
_schedule();
// return a copy of the configuration options
return {
rate: rate
};
};
/**
* Set the time of the timer. To get the current time, use getTime() or now().
* @param {number | Date} time The time in hyper-time.
*/
timer.setTime = function (time) {
if (time instanceof Date) {
hyperTime = time.valueOf();
}
else {
var newTime = Number(time);
if (isNaN(newTime)) {
throw new TypeError('time must be a Date or number');
}
hyperTime = newTime;
}
// reschedule running timeouts
_schedule();
};
/**
* Returns the current time of the timer as a number.
* See also getTime().
* @return {number} The time
*/
timer.now = function () {
if (running) {
// TODO: implement performance.now() / process.hrtime(time) for high precision calculation of time interval
var realInterval = util.nowReal() - realTime;
var hyperInterval = realInterval * rate;
return hyperTime + hyperInterval;
}
else {
return hyperTime;
}
};
/**
* Continue the timer.
*/
timer['continue'] = function() {
realTime = util.nowReal();
running = true;
// reschedule running timeouts
_schedule();
};
/**
* Pause the timer. The timer can be continued again with `continue()`
*/
timer.pause = function() {
hyperTime = timer.now();
realTime = null;
running = false;
// reschedule running timeouts (pauses them)
_schedule();
};
/**
* Returns the current time of the timer as Date.
* See also now().
* @return {Date} The time
*/
// rename to getTime
timer.getTime = function() {
return new Date(timer.now());
};
/**
* Get the value of the hypertimer. This function returns the result of getTime().
* @return {Date} current time
*/
timer.valueOf = timer.getTime;
/**
* Return a string representation of the current hyper-time.
* @returns {string} String representation
*/
timer.toString = function () {
return timer.getTime().toString();
};
/**
* Set a timeout, which is triggered when the timeout occurs in hyper-time.
* See also setTrigger.
* @param {Function} callback Function executed when delay is exceeded.
* @param {number} delay The delay in milliseconds. When the rate is
* zero, or the delay is smaller or equal to
* zero, the callback is triggered immediately.
* @return {number} Returns a timeoutId which can be used to cancel the
* timeout using clearTimeout().
*/
timer.setTimeout = function(callback, delay) {
var id = idSeq++;
var timestamp = timer.now() + delay;
if (isNaN(timestamp)) {
throw new TypeError('delay must be a number');
}
// add a new timeout to the queue
_queueTimeout({
id: id,
type: TYPE.TIMEOUT,
time: timestamp,
callback: callback
});
// reschedule the timeouts
_schedule();
return id;
};
/**
* Set a trigger, which is triggered when the timeout occurs in hyper-time.
* See also getTimeout.
* @param {Function} callback Function executed when timeout occurs.
* @param {Date | number} time An absolute moment in time (Date) when the
* callback will be triggered. When the rate is
* zero, or the date is a Date in the past,
* the callback is triggered immediately.
* @return {number} Returns a triggerId which can be used to cancel the
* trigger using clearTrigger().
*/
timer.setTrigger = function (callback, time) {
var id = idSeq++;
var timestamp = Number(time);
if (isNaN(timestamp)) {
throw new TypeError('time must be a Date or number');
}
// add a new timeout to the queue
_queueTimeout({
id: id,
type: TYPE.TRIGGER,
time: timestamp,
callback: callback
});
// reschedule the timeouts
_schedule();
return id;
};
/**
* Trigger a callback every interval. Optionally, a start date can be provided
* to specify the first time the callback must be triggered.
* See also setTimeout and setTrigger.
* @param {Function} callback Function executed when delay is exceeded.
* @param {number} interval Interval in milliseconds. When interval
* is smaller than zero or is infinity, the
* interval will be set to zero and triggered
* with a maximum rate.
* @param {Date | number} [firstTime] An absolute moment in time (Date) when the
* callback will be triggered the first time.
* By default, firstTime = now() + interval.
* @return {number} Returns a intervalId which can be used to cancel the
* trigger using clearInterval().
*/
timer.setInterval = function(callback, interval, firstTime) {
var id = idSeq++;
var _interval = Number(interval);
if (isNaN(_interval)) {
throw new TypeError('interval must be a number');
}
if (_interval < 0 || !isFinite(_interval)) {
_interval = 0;
}
var timestamp;
if (firstTime != undefined) {
timestamp = Number(firstTime);
if (isNaN(timestamp)) {
throw new TypeError('firstTime must be a Date or number');
}
}
else {
// firstTime is undefined or null
timestamp = (timer.now() + _interval);
}
// add a new timeout to the queue
_queueTimeout({
id: id,
type: TYPE.INTERVAL,
time: timestamp,
interval: _interval,
//firstTime: timestamp,
//occurrence: 0,
callback: callback
});
// reschedule the timeouts
_schedule();
return id;
};
/**
* Cancel a timeout
* @param {number} timeoutId The id of a timeout
*/
timer.clearTimeout = function(timeoutId) {
// find the timeout in the queue
for (var i = 0; i < timeouts.length; i++) {
if (timeouts[i].id === timeoutId) {
// remove this timeout from the queue
timeouts.splice(i, 1);
// reschedule timeouts
_schedule();
break;
}
}
};
/**
* Cancel a trigger
* @param {number} triggerId The id of a trigger
*/
timer.clearTrigger = timer.clearTimeout;
timer.clearInterval = timer.clearTimeout;
/**
* Returns a list with the id's of all timeouts
* @returns {number[]} Timeout id's
*/
timer.list = function () {
return timeouts.map(function (timeout) {
return timeout.id;
});
};
/**
* Clear all timeouts
*/
timer.clear = function () {
// empty the queue
timeouts = [];
// reschedule
_schedule();
};
/**
* Add a timeout to the queue. After the queue has been changed, the queue
* must be rescheduled by executing _reschedule()
* @param {{type: number, time: number, callback: Function}} params
* @private
*/
function _queueTimeout(params) {
// insert the new timeout at the right place in the array, sorted by time
if (timeouts.length > 0) {
var i = timeouts.length - 1;
while (i >= 0 && timeouts[i].time > params.time) {
i--;
}
// insert the new timeout in the queue. Note that the timeout is
// inserted *after* existing timeouts with the exact *same* time,
// so the order in which they are executed is deterministic
timeouts.splice(i + 1, 0, params);
}
else {
// queue is empty, append the new timeout
timeouts.push(params);
}
}
/**
* Reschedule all queued timeouts
* @private
*/
function _schedule() {
var timeout;
var next = timeouts[0];
// cancel timer when running
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
if (running && next) {
// schedule next timeout
var time = next.time;
var delay = time - timer.now();
var realDelay = delay / rate;
function trigger() {
// execute all expired timeouts
var intervals = [];
while (timeouts.length > 0 &&
((timeouts[0].time <= time) || !isFinite(timeouts[0].time))) {
timeout = timeouts[0];
// execute the callback
try {
timeout.callback();
} catch (err) {
// silently ignore errors thrown by the callback
}
// in case of an interval we have to reschedule on next cycle
if (timeout.type === TYPE.INTERVAL && timeouts[0] === timeout) {
// timeout is not removed inside the callback, reschedule it
intervals.push(timeout);
}
timeouts.shift();
}
// reschedule intervals
for (var i = 0; i < intervals.length; i++) {
timeout = intervals[i];
// FIXME: adding the interval each occurrence will give round-off errors.
// however, when multliplying the firstTime with the number of occurrences,
// we cannot easily swith the rate at any time.
//timeout.occurrence++;
//timeout.time = timeout.firstTime + timeout.interval * timeout.occurrence * (rate < 0 ? -1 : 1);
timeout.time += timeout.interval * (rate < 0 ? -1 : 1);
_queueTimeout(timeout);
}
// initialize next round of timeouts
_schedule();
}
timeoutId = setTimeout(trigger, realDelay);
}
}
Object.defineProperty(timer, 'running', {
get: function () {
return running;
}
});
timer.config(options); // apply options
timer.setTime(util.nowReal()); // set time as current real time
timer.continue(); // start the timer
return timer;
}
module.exports = hypertimer;
{
"name": "hypertimer",
"version": "0.0.1",
"description": "A timer running faster or slower than realtime, in continuous or discrete time",
"version": "0.1.0",
"description": "A timer running faster or slower than real-time, in continuous or discrete time",
"author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)",

@@ -10,6 +10,9 @@ "main": "./index",

"time",
"realtime",
"simulation",
"hypertime",
"hyper-time",
"real-time",
"continuous",
"discrete"
"discrete",
"event"
],

@@ -22,2 +25,3 @@ "repository": {

"devDependencies": {
"async": "^0.9.0",
"browserify": "^4.2.0",

@@ -24,0 +28,0 @@ "gulp": "^3.8.6",

hypertimer
==========
A timer running faster or slower than realtime, in continuous or discrete time.
Hypertimer is a timer running in [hypertime](http://en.wikipedia.org/wiki/Hypertime), at a different rate than [real-time](http://en.wikipedia.org/wiki/Real-time_clock), or in discrete time. Hypertimer is useful for controlling the time in simulations.
Hypertimer offers basic functionality to get the time and set timeouts:
- `getTime()`, `setTime()`, and `now()`, to get and set the time of the timer.
- `setTimeout()`, `setInterval()`, and `setTrigger()` to set timeouts.
These functions are similar to and compatible with JavaScripts built-in functions `Date.now()`,
`setTimeout()`, and `setInterval()`, but there is an important difference: they can run with a different current time and at a different rate.
## Install

@@ -12,9 +21,176 @@

Install via bower:
bower install hypertimer
## Load
### node.js
```js
var hypertimer = require('hypertimer');
var timer = hypertimer({rate: 1});
timer.setTime(new Date(2014, 0, 1));
```
### browser
```html
<!doctype html>
<html>
<head>
<script src="./dist/hypertimer.min.js"></script>
</head>
<body>
<script>
var timer = hypertimer({rate: 1});
timer.setTime(new Date(2014, 0, 1));
</script>
</body>
</html>
```
## Use
coming soon...
The following example shows how to create a hypertimer and set a timeout.
```js
// create a hypertimer running ten times faster than real-time
var timer = hypertimer({rate: 10});
// start the timer at 1st of January 2020
timer.setTime(new Date(2020, 0, 1, 12, 0, 0));
// set a timeout after a delay
var delay = 10000; // milliseconds (hyper-time)
timer.setTimeout(function () {
console.log('Timeout!');
console.log('It is now ' + delay + ' ms later in hyper-time, ' +
'the time is: ' + timer.getTime());
}, delay);
console.log('The time is: ' + timer.getTime());
```
## API
### Construction
A hypertimer is constructed as:
```js
hypertimer([options])
```
By default, a new hypertimer runs with real-time speed and time.
Available options:
Name | Type | Default | Description
---- | ------ | ------- | -----------
rate | Number | 1 | The rate (in milliseconds per millisecond) at which the timer runs, with respect to real-time speed. Can be a positive or negative value.
Example:
```js
var timer = hypertimer({rate: 10});
```
### Properties
- `running`
True when the timer is running, false when paused. See also functions `pause()` and `continue()`.
### Methods
- **`clear()`**
Clear all running timeouts.
- **`clearTimeout(timeoutId: number)`**
Cancel a timeout.
- **`clearInterval(intervalId: number)`**
Cancel an interval.
- **`clearTrigger(triggerId: number)`**
Cancel a trigger.
- **`config([options: Object]): Object`**
Change the configuration options of the hypertimer, and/or retrieve the current configuration. Available options:
- `rate: Number`
The rate (in milliseconds per millisecond) at which the timer runs, with respect to real-time speed. By default, rate is 1. Note that rate can even be a negative number.
- **`continue()`**
Continue the timer when paused. The state of the timer can be retrieved via the property `running`. See also `pause()`.
- **`getTime(): Date`**
Returns the current time of the timer as Date. See also `now()`.
The time can be set using `setTime(time)`.
- **`list()`**
Returns a list with the id's of all running timeouts.
- **`now() : number`**
Returns the current time of the timer as a number. See also `getTime()`.
- **`pause()`**
Pause the timer. The state of the timer can be retrieved via the property `running`. See also `continue()`.
- **`setInterval(callback: Function, interval: number [, firstTime: Date | number])`**
Trigger a callback every interval. Optionally, a start date can be provided
to specify the first time the callback must be triggered. The function returns an intervalId which can be used to cancel the trigger using `clearInterval()`. See also `setTimeout` and `setTrigger`. Parameters:
- `callback: Function`
Function executed when delay is exceeded.
- `interval: number`
Interval in milliseconds. When interval is smaller than zero or is infinity, the interval will be set to zero and triggered with a maximum rate.
- `[firstTime: Date | number]`
An optional absolute moment in time (Date) when the callback will be triggered the first time. By default, `firstTime = now() + interval`.
- **`setTime(time: number | Date)`**
Set the current time of the timer. Can be a Date or a timestamp. To get the current time, use `getTime()` or `now()`.
- **`setTimeout(callback: Function, delay: number) : number`**
Set a timeout, which is triggered when the timeout occurs in hyper-time. See also `setTrigger` and `setInterval`. The function returns a timeoutId, which can be used to cancel the timeout using `clearTimeout(timeoutId)`. The parameters are:
- `callback: Function`
Function executed when the delay is exceeded
- `delay: number`
The delay in milliseconds. When the rate is zero, or the delay is smaller or equal to zero, the callback is triggered immediately.
- **`setTrigger(callback: Function, time: Date | number) : number`**
Set a trigger, which is triggered when the timeout occurs in hyper-time. See also `getTimeout`. The function returns a triggerId which can be used to cancel the trigger using `clearTrigger()`. The parameters are:
- `callback: Function`
Function executed when delay is exceeded.
- `time: Date | number`
An absolute moment in time (Date) when the callback will be triggered. When the rate is zero, or the date is a Date in the past, the callback is triggered immediately.
- **`toString() : String`**
Return a string representation of the current hyper-time, equal to `timer.getTime().toString()`.
- **`valueOf() : Date`**
Get the value of the hypertimer, returns the current time of the timer as Date.
## Examples
Examples can be found here:
https://github.com/enmasseio/hypertimer/tree/master/examples
## Roadmap
- Implement support for discrete time (discrete, deterministic events).
- Implement a scalable solution to synchronize hypertimers running in different
processes.
## Build

@@ -21,0 +197,0 @@

@@ -0,5 +1,842 @@

// NOTE: all timeouts should have time differences of at least 50ms to be
// safely distinguishably
var assert = require('assert');
var async = require('async');
var hypertimer = require('../lib/hypertimer');
/**
* Assert whether two dates are approximately equal.
* Throws an assertion error when the dates are not approximately equal.
* @param {Date} date1
* @param {Date} date2
* @param {Number} [epsilon=25] maximum difference in milliseconds
*/
function approx(date1, date2, epsilon) {
assert(Math.abs(date1 - date2) < (epsilon === undefined ? 25 : epsilon),
date1.toISOString() + ' ~= ' + date2.toISOString());
}
describe('approx', function () {
it ('should compare two dates', function () {
var a = new Date();
var b = new Date(a.valueOf() + 0);
var c = new Date(a.valueOf() + 30);
var d = new Date(a.valueOf() + 300);
approx(a, b);
assert.throws(function() {approx(a, c)});
approx(a, c, 100);
assert.throws(function() {approx(a, d, 100)});
});
});
describe('hypertimer', function () {
describe('config', function () {
it('should get configuration', function () {
var timer = hypertimer();
assert.deepEqual(timer.config(), {rate: 1});
});
it('should set configuration', function () {
var timer = hypertimer();
assert.equal(timer.config().rate, 1);
timer.config({rate: 10});
assert.equal(timer.config().rate, 10);
});
it('should set empty configuration', function () {
var timer = hypertimer();
assert.equal(timer.config().rate, 1);
timer.config({});
assert.equal(timer.config().rate, 1);
});
it('should set configuration on creation', function () {
var timer = hypertimer({rate: 10});
assert.equal(timer.config().rate, 10);
});
it('should update configuration', function () {
var timer = hypertimer({rate: 1});
timer.setTime(new Date(2050,0,1,12,0,0,0));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,0));
timer.config({rate: 10});
assert.equal(timer.config().rate, 10);
approx(timer.getTime(), new Date(2050,0,1,12,0,0,0));
});
it('should throw an error on invalid rate', function () {
assert.throws(function () {
hypertimer({rate: 'bla'});
}, /TypeError: rate must be a number/);
});
});
describe('get/set time', function () {
it ('should set the current hyper-time from a Date', function () {
var timer = hypertimer({rate: 1});
timer.setTime(new Date(2050, 0, 1));
approx(timer.getTime(), new Date(2050, 0, 1));
});
it ('should set the current hyper-time from a number', function () {
var timer = hypertimer({rate: 1});
timer.setTime(new Date(2050, 0, 1).valueOf());
approx(timer.getTime(), new Date(2050, 0, 1));
});
it ('should throw an error in case of invalid variable', function () {
var timer = hypertimer({rate: 1});
assert.throws(function () {timer.setTime('bla')}, /time must be a Date or number/);
assert.throws(function () {timer.setTime({})}, /time must be a Date or number/);
});
it ('should get the current hyper-time as Date', function () {
var timer = hypertimer({rate: 1});
assert(timer.getTime() instanceof Date, 'must return a Date');
approx(timer.getTime(), new Date());
});
it ('should get the current hyper-time as number', function () {
var timer = hypertimer({rate: 1});
assert(typeof timer.now(), 'number');
approx(new Date(timer.now()), new Date());
});
});
describe('run', function () {
it('should start running by default', function () {
var timer = hypertimer({rate: 1});
assert.equal(timer.running, true);
});
it('should test whether running', function () {
var timer = hypertimer({rate: 1});
assert.equal(timer.running, true);
timer.pause();
assert.equal(timer.running, false);
timer.continue();
assert.equal(timer.running, true);
});
it('start should continue where it was left off', function (done) {
var timer = hypertimer({rate: 1});
timer.pause();
var a = timer.getTime();
approx(timer.getTime(), new Date());
setTimeout(function () {
timer.continue(); // continue
approx(timer.getTime(), a);
setTimeout(function () {
approx(timer.getTime(), new Date(a.valueOf() + 100));
done();
}, 100);
}, 100);
});
it('should set a new time via set', function () {
var d = new Date(2050,0,1);
var timer = hypertimer({rate: 1});
approx(timer.getTime(), new Date());
timer.setTime(d);
approx(timer.getTime(), d);
});
it('time should not change when paused', function (done) {
var d = new Date(2050,0,1);
var timer = hypertimer({rate: 1});
timer.setTime(d);
approx(timer.getTime(), d);
timer.pause();
approx(timer.getTime(), d);
setTimeout(function () {
approx(timer.getTime(), d);
done();
}, 100);
});
it('should run hyper-time with the configured rate', function (done) {
// To keep the test fast, don't see a rate too large, else you have to
// increase the delay to compensate for possible round-off errors and
// inaccuracy of the real-time.
var rates = [1, 2, 1/2, -1, -2, 0];
var epsilon = 20;
async.map(rates, function (rate, cb) {
var timer = hypertimer({rate: rate});
var started = new Date();
approx(timer.getTime(), started);
var delay = 200;
setTimeout(function () {
approx(timer.getTime(), new Date(started.valueOf() + delay * rate), epsilon);
cb();
}, delay);
}, done);
});
});
describe('timeout', function () {
it('should set a timeout with rate=1', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 100));
done();
}, 100);
});
it('should set a timeout with rate=2', function (done) {
var timer = hypertimer({rate: 2});
var start = new Date();
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 100));
done();
}, 200);
});
it('should set a timeout with rate=1/2', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 200));
done();
}, 100);
});
it('should set a timeout with a delay in the past', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTimeout(function () {
approx(new Date(), start);
done();
}, -10);
});
it('should set a timeout with an infinite delay', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTimeout(function () {
approx(new Date(), start);
done();
}, Infinity);
});
it('should execute multiple timeouts in the right order', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var log = [];
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 200));
log.push('B');
assert.deepEqual(log, ['A', 'B']);
}, 100);
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 300));
log.push('C');
assert.deepEqual(log, ['A', 'B', 'C']);
done();
}, 150);
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 100));
log.push('A');
assert.deepEqual(log, ['A']);
}, 50);
});
it('should pause a timeout when the timer is paused', function (done) {
var timer = hypertimer({rate: 1/2});
timer.setTime(new Date(2050,0,1,12,0,0,0));
var start = new Date();
var log = [];
timer.setTimeout(function () {
assert.deepEqual(log, ['A', 'B']);
approx(new Date(), new Date(start.valueOf() + 400));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,100));
done();
}, 100);
// real-time timeout
setTimeout(function () {
log.push('A');
timer.pause();
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
approx(new Date(), new Date(start.valueOf() + 100));
setTimeout(function () {
log.push('B');
timer.continue();
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
approx(new Date(), new Date(start.valueOf() + 300));
}, 200);
}, 100);
});
it('should adjust a timeout when the timers time is adjusted', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTime(new Date(2050,0,1,12,0,0,0));
timer.setTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 100));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,200));
done();
}, 200);
timer.setTime(new Date(2050,0,1,12,0,0,100));
});
it('should adjust a timeout when the timers rate is adjusted', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var log = [];
timer.setTime(new Date(2050,0,1,12,0,0,0));
timer.setTimeout(function () {
assert.deepEqual(log, ['A']);
approx(new Date(), new Date(start.valueOf() + 150));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,100));
done();
}, 100);
setTimeout(function () {
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
timer.config({rate: 1});
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
log.push('A');
}, 100)
});
it('should cancel a timeout with clearTimeout', function (done) {
var timer = hypertimer({rate: 1});
var log = [];
timer.setTime(new Date(2050,0,1,12,0,0,0));
var timeout1 = timer.setTimeout(function () {
log.push('1');
}, 100);
var timeout2 = timer.setTimeout(function () {
log.push('2');
assert(false, 'should not trigger timeout1')
}, 150);
var timeout3 = timer.setTimeout(function () {
log.push('3');
}, 200);
setTimeout(function () {
timer.clearTimeout(timeout2);
}, 50);
setTimeout(function () {
assert.deepEqual(log, ['1', '3']);
done();
}, 250)
});
it('should be able to use setTimout from a different context', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var mySetTimeout = timer.setTimeout;
mySetTimeout(function () {
approx(new Date(), new Date(start.valueOf() + 200));
done();
}, 100);
});
});
describe('trigger', function () {
it('should set a trigger with rate=1', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var time = new Date(new Date().valueOf() + 100);
timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 100));
done();
}, time);
});
it('should set a trigger with rate=2', function (done) {
var timer = hypertimer({rate: 2});
var start = new Date();
var time = new Date(new Date().valueOf() + 200);
timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 100));
done();
}, time);
});
it('should set a trigger with rate=1/2', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var time = new Date(new Date().valueOf() + 100);
timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 200));
done();
}, time);
});
it('should set a trigger with a time in the past', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var time = new Date(new Date().valueOf() - 100);
timer.setTrigger(function () {
approx(new Date(), start);
done();
}, time);
});
it('should set a trigger with a number as time', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var time = new Date().valueOf() + 100;
timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 100));
done();
}, time);
});
it('should set a trigger with a number in the past as time', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var time = new Date().valueOf() - 100;
timer.setTrigger(function () {
approx(new Date(), start);
done();
}, time);
});
it('should set a trigger with an infinite number as time', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var time = Infinity;
timer.setTrigger(function () {
approx(new Date(), start);
done();
}, time);
});
it('should set a trigger with a start and end', function (done) {
var timer = hypertimer({rate: 2});
var start = new Date(2050,0,1,12,0,0,0);
var end = new Date(2050,0,1,12,0,0,200);
timer.setTime(start);
var a = new Date();
timer.setTrigger(function () {
approx(new Date(), new Date(a.valueOf() + 100));
done();
}, end);
});
it('should set a trigger with a start and an end in the past', function (done) {
var timer = hypertimer({rate: 2});
var start = new Date(2050,0,1,12,0,0,0);
var end = new Date(2050,0,1,11,0,0,0);
timer.setTime(start);
var a = new Date();
timer.setTrigger(function () {
approx(new Date(), a);
done();
}, end);
});
it('should execute multiple triggers in the right order', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var log = [];
timer.setTime(new Date(2050,0,1,12,0,0,0));
var triggerB = timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 200));
log.push('B');
assert.deepEqual(log, ['A', 'B']);
}, new Date(2050,0,1,12,0,0,100));
var triggerC = timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 300));
log.push('C');
assert.deepEqual(log, ['A', 'B', 'C']);
done();
}, new Date(2050,0,1,12,0,0,150));
var triggerA = timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 100));
log.push('A');
assert.deepEqual(log, ['A']);
}, new Date(2050,0,1,12,0,0,50));
assert.deepEqual(timer.list(), [triggerA, triggerB, triggerC]);
});
it('should pause a trigger when the timer is paused', function (done) {
var timer = hypertimer({rate: 1/2});
timer.setTime(new Date(2050,0,1,12,0,0,0));
var start = new Date();
var log = [];
timer.setTrigger(function () {
assert.deepEqual(log, ['A', 'B']);
approx(new Date(), new Date(start.valueOf() + 400));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,100));
done();
}, new Date(2050,0,1,12,0,0,100));
// real-time timeout
setTimeout(function () {
log.push('A');
timer.pause();
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
approx(new Date(), new Date(start.valueOf() + 100));
setTimeout(function () {
log.push('B');
timer.continue();
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
approx(new Date(), new Date(start.valueOf() + 300));
}, 200);
}, 100);
});
it('should adjust a trigger when the timers time is adjusted', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTime(new Date(2050,0,1,12,0,0,0));
timer.setTrigger(function () {
approx(new Date(), new Date(start.valueOf() + 100));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,200));
done();
}, new Date(2050,0,1,12,0,0,200));
timer.setTime(new Date(2050,0,1,12,0,0,100));
});
it('should adjust a trigger when the timers rate is adjusted', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var log = [];
timer.setTime(new Date(2050,0,1,12,0,0,0));
timer.setTrigger(function () {
assert.deepEqual(log, ['A']);
approx(new Date(), new Date(start.valueOf() + 150));
approx(timer.getTime(), new Date(2050,0,1,12,0,0,100));
done();
}, new Date(2050,0,1,12,0,0,100));
setTimeout(function () {
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
timer.config({rate: 1});
approx(timer.getTime(), new Date(2050,0,1,12,0,0,50));
log.push('A');
}, 100)
});
it('should cancel a trigger with clearTrigger', function (done) {
var timer = hypertimer({rate: 1});
var log = [];
timer.setTime(new Date(2050,0,1,12,0,0,0));
var trigger1 = timer.setTrigger(function () {
log.push('1');
}, new Date(2050,0,1,12,0,0,100));
var trigger2 = timer.setTrigger(function () {
log.push('2');
assert(false, 'should not trigger trigger1')
}, new Date(2050,0,1,12,0,0,150));
var trigger3 = timer.setTrigger(function () {
log.push('3');
}, new Date(2050,0,1,12,0,0,200));
setTimeout(function () {
timer.clearTrigger(trigger2);
}, 50);
setTimeout(function () {
assert.deepEqual(log, ['1', '3']);
done();
}, 250)
});
});
describe('interval', function () {
it('should set an interval', function (done) {
var timer = hypertimer({rate: 1/2});
var start = new Date();
var occurrence = 0;
var interval = timer.setInterval(function () {
occurrence++;
approx(new Date(), new Date(start.valueOf() + occurrence * 100));
approx(timer.getTime(), new Date(start.valueOf() + occurrence * 50));
if (occurrence == 3) {
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
done();
}
}, 50);
});
it('should set an interval with firstTime', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTime(new Date(2050,0,1,12,0,0,0));
var firstTime = new Date(2050,0,1,12,0,0,300);
var occurrence = 0;
var interval = timer.setInterval(function () {
occurrence++;
approx(new Date(), new Date(start.valueOf() + 300 + (occurrence - 1) * 100));
approx(timer.getTime(), new Date(firstTime.valueOf() + (occurrence - 1) * 100));
if (occurrence == 3) {
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
done();
}
}, 100, firstTime);
});
it('should set an interval with a number as firstTime', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTime(new Date(2050,0,1,12,0,0,0));
var firstTime = new Date(2050,0,1,12,0,0,300);
var occurrence = 0;
var interval = timer.setInterval(function () {
occurrence++;
approx(new Date(), new Date(start.valueOf() + 300 + (occurrence - 1) * 100));
approx(timer.getTime(), new Date(firstTime.valueOf() + (occurrence - 1) * 100));
if (occurrence == 4) {
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
done();
}
}, 100, firstTime.valueOf());
});
it('should clear an interval', function (done) {
var timer = hypertimer({rate: 1});
var interval = timer.setInterval(function () {
assert(false, 'should not trigger interval')
}, 100);
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
// wait until the time where the interval should have been triggered
setTimeout(function () {
done();
}, 200);
});
it('should set a negative interval', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var occurrence = 0;
var interval = timer.setInterval(function () {
occurrence++;
approx(new Date(), start);
approx(timer.getTime(), start);
if (occurrence == 4) {
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
done();
}
}, -100);
});
it('should set a negative interval with firstTime', function (done) {
var timer = hypertimer({rate: 1});
var timerStart = new Date(2050,0,1,12,0,0,0);
var realStart = new Date(new Date().valueOf() + 200);
var firstStart = new Date(2050,0,1,12,0,0,200);
timer.setTime(timerStart);
var occurrence = 0;
var interval = timer.setInterval(function () {
occurrence++;
approx(new Date(), realStart);
approx(timer.getTime(), firstStart);
if (occurrence == 4) {
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
done();
}
}, -100, firstStart);
});
it('should set an infinite interval', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
var occurrence = 0;
var interval = timer.setInterval(function () {
occurrence++;
approx(new Date(), start);
approx(timer.getTime(), start);
if (occurrence == 4) {
timer.clearInterval(interval);
assert.deepEqual(timer.list(), []);
done();
}
}, Infinity);
});
it('should correctly update interval when rate changes', function (done) {
var timer = hypertimer({rate: 1});
var start = new Date();
timer.setTime(new Date(2050,0,1, 12,0,0, 0));
var plans = {
'1': {realTime: new Date(start.valueOf() + 100), hyperTime: new Date(2050,0,1, 12,0,0, 100), newRate: 2},
'2': {realTime: new Date(start.valueOf() + 150), hyperTime: new Date(2050,0,1, 12,0,0, 200), newRate: 1/2},
'3': {realTime: new Date(start.valueOf() + 350), hyperTime: new Date(2050,0,1, 12,0,0, 300), newRate: -1},
'4': {realTime: new Date(start.valueOf() + 450), hyperTime: new Date(2050,0,1, 12,0,0, 200), newRate: 1},
'5': {realTime: new Date(start.valueOf() + 550), hyperTime: new Date(2050,0,1, 12,0,0, 300)}
};
var occurrence = 0;
timer.setInterval(function () {
occurrence++;
var plan = plans[occurrence];
try {
approx(timer.getTime(), plan.hyperTime);
approx(new Date(), plan.realTime);
}
catch (err) {
done(err);
}
if ('newRate' in plan) {
timer.config({rate: plan.newRate});
}
if (!plans[occurrence + 1]) {
timer.clear();
done();
}
}, 100);
});
});
// TODO: test with a numeric time instead of "real" Dates, timer.setTime(0), rate='discrete', and timeouts like timer.timeout(cb, 1)
it('should get valueOf', function () {
var timer = hypertimer();
assert(timer.valueOf() instanceof Date);
approx(timer.valueOf(), timer.getTime());
});
it('should get toString', function () {
var timer = hypertimer();
assert.strictEqual(typeof timer.toString(), 'string');
assert.strictEqual(timer.toString().toString(), timer.getTime().toString());
});
it('should list all timeouts', function (done) {
var timer = hypertimer({rate: 1});
var id1 = timer.setTimeout(function () {}, 1000);
var id2 = timer.setTimeout(function () {}, 50);
var id3 = timer.setTrigger(function () {}, new Date(Date.now() + 100));
assert.deepEqual(timer.list(), [id2, id3, id1]);
timer.clearTimeout(id2);
assert.deepEqual(timer.list(), [id3, id1]);
setTimeout(function () {
assert.deepEqual(timer.list(), [id1]);
timer.clearTimeout(id1);
assert.deepEqual(timer.list(), []);
done();
}, 150);
});
it('should clear all timeouts', function () {
var timer = hypertimer({rate: 1});
var id1 = timer.setTimeout(function () {}, 1000);
var id2 = timer.setTimeout(function () {}, 50);
var id3 = timer.setTrigger(function () {}, new Date(Date.now() + 100));
assert.deepEqual(timer.list(), [id2, id3, id1]);
timer.clear();
assert.deepEqual(timer.list(), []);
});
// FIXME: interval stops when switching to negative rate and back
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc