├── .npmignore ├── .gitignore ├── .travis.yml ├── LICENSE ├── package.json ├── README.md ├── index.js └── test.js /.npmignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | test.js 3 | .* 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.8" 5 | - "0.10" 6 | - "0.12" 7 | - "iojs" 8 | before_install: 9 | - 'if [ "${TRAVIS_NODE_VERSION}" == "0.8" ]; then npm install -g npm@2.7.3; fi' 10 | script: 11 | - "npm run test-travis" 12 | after_script: 13 | - "npm install coveralls@2.11.x && cat coverage/lcov.info | coveralls" 14 | notifications: 15 | irc: 16 | channels: 17 | - "irc.freenode.org#unshift" 18 | on_success: change 19 | on_failure: change 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tick-tock", 3 | "version": "1.0.0", 4 | "description": "Timer management, never forget to clear timers again", 5 | "main": "index.js", 6 | "scripts": { 7 | "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", 8 | "test": "mocha test.js", 9 | "watch": "mocha --watch test.js", 10 | "coverage": "istanbul cover ./node_modules/.bin/_mocha -- test.js", 11 | "test-travis": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- test.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/unshiftio/tick-tock" 16 | }, 17 | "keywords": [ 18 | "timer", 19 | "setTimeout", 20 | "clearTimeout", 21 | "setInterval", 22 | "clearInterval", 23 | "setImmediate", 24 | "clearImmediate", 25 | "tick", 26 | "tock", 27 | "nextTick", 28 | "time" 29 | ], 30 | "author": "Arnout Kazemier", 31 | "license": "MIT", 32 | "devDependencies": { 33 | "assume": "~1.5.0", 34 | "istanbul": "~0.4.5", 35 | "mocha": "~3.5.0", 36 | "pre-commit": "~1.2.0" 37 | }, 38 | "dependencies": { 39 | "millisecond": "0.1.x" 40 | }, 41 | "pre-commit": ["coverage", "100%"] 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tick-tock 2 | 3 | [![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](http://img.shields.io/npm/v/tick-tock.svg?style=flat-square)](http://browsenpm.org/package/tick-tock)[![Build Status](http://img.shields.io/travis/unshiftio/tick-tock/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/tick-tock)[![Dependencies](https://img.shields.io/david/unshiftio/tick-tock.svg?style=flat-square)](https://david-dm.org/unshiftio/tick-tock)[![Coverage Status](http://img.shields.io/coveralls/unshiftio/tick-tock/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/tick-tock?branch=master)[![IRC channel](http://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](http://webchat.freenode.net/?channels=unshift) 4 | 5 | `tick-tock` is a small timer and `setTimeout` management library. Nothing to 6 | fancy, but fancy enough to make your code more readable. 7 | 8 | ## Installation 9 | 10 | This library can be used with both browserify and node.js and can be installed 11 | using npm: 12 | 13 | ``` 14 | npm install --save tick-tock 15 | ``` 16 | 17 | ## Usage 18 | 19 | In all example we assume that you've required and initialized the library using: 20 | 21 | ```js 22 | 'use strict'; 23 | 24 | var Tick = require('tick-tock') 25 | , tock = new Tick(); 26 | ``` 27 | 28 | All methods return `this` unless stated otherwise. The constructor can be 29 | initialized with 1 argument: 30 | 31 | 1. `context` This is the default context in which each `setTimeout` or 32 | `setInterval` function is executed (it sets the `this` value). If nothing is 33 | supplied it will default to your `tick-tock` instance. 34 | 35 | The following methods are available on your constructed instance: 36 | 37 | - [Tock.setTimeout(name, fn, timeout)](#tocksettimeout) 38 | - [Tock.setInterval(name, fn, interval)](#tocksetinterval) 39 | - [Tock.clear(name, name, ..)](#tockclear) 40 | - [Tock.active(name)](#tockactive) 41 | - [Tock.adjust(name, duration)](#tockadjust) 42 | - [Tock.end()](#tockend) 43 | 44 | ### Tock.setTimeout() 45 | 46 | The `setTimeout` method adds as you might have expected.. a new setTimeout. The 47 | timeouts are stored based on the name that your provide them. If you've already 48 | stored a timer with the given name, it will add the supplied callback to the 49 | same stack so only one timer is used and they all run at the same time. Normally 50 | you would supply the `setTimeout` method with a number indicating long it should 51 | timeout. In this library we also support human readable strings. 52 | 53 | ```js 54 | tock.setTimeout('foo', function () {}, 10); 55 | 56 | // Ran at the same point in time as the timeout above 57 | setTimeout(function () { 58 | tock.setTimeout('foo', function () {}, 10); 59 | }, 5); 60 | 61 | tock.setTimeout('another', function () {}, '10 minutes'); 62 | ``` 63 | 64 | ### Tock.setInterval() 65 | 66 | Exactly the same method and functionality as above but instead of only being 67 | called once, it will called at an interval. 68 | 69 | ### Tock.clear() 70 | 71 | The `clear` method allows you to clear every stored timeout by name. You can 72 | supply it multiple arguments (strings) to clear all given timers and if you 73 | supply 1 strings it can be comma separated list of names. If no arguments are 74 | supplied it will clear all timers in this instance. 75 | 76 | ```js 77 | tock.clear('foo', 'bar'); 78 | tock.clear('foo, bar'); // Same as above. 79 | tock.clear(); // Nuke everything. 80 | ``` 81 | 82 | ### Tock.active() 83 | 84 | Check if there's an active timer for the given name and returns a boolean. 85 | 86 | ```js 87 | tock.active('foo'); // true; 88 | tock.clear(); 89 | tock.active('foo'); // false; 90 | ``` 91 | 92 | ### Tock.adjust() 93 | 94 | There are cases where you sometimes need to update or change the interval of an 95 | `setTimeout` or `setInterval` for example in the case of a setTimeout which 96 | coordinates a heartbeat. In order to make this easier you call the `.adjust` 97 | method with the name of the timeout that you want to adjust and the new 98 | interval/timeout. 99 | 100 | ```js 101 | tock.setTimeout('heartbeat timeout', function () {}); 102 | 103 | // you recieved a new heartbeat so you want to reset or adjust the heartbeat; 104 | tock.adjust('heartbeat timeout', '1 second'); 105 | ``` 106 | 107 | ### Tock.end() 108 | 109 | You no longer wish to interact with your instance and wants it to be fully shut 110 | down. This kills all active timers using `tock.clear()` and nulls the internal 111 | properties. It will return `true` if it's the first time it's destroyed and 112 | `false` if was already destroyed before. If you call any of the other methods 113 | after destruction, they will throw errors. 114 | 115 | ```js 116 | tock.end(); 117 | ``` 118 | 119 | ## License 120 | 121 | MIT 122 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var has = Object.prototype.hasOwnProperty 4 | , ms = require('millisecond'); 5 | 6 | /** 7 | * Timer instance. 8 | * 9 | * @constructor 10 | * @param {Object} timer New timer instance. 11 | * @param {Function} clear Clears the timer instance. 12 | * @param {Function} duration Duration of the timer. 13 | * @param {Function} fn The functions that need to be executed. 14 | * @api private 15 | */ 16 | function Timer(timer, clear, duration, fn) { 17 | this.start = +(new Date()); 18 | this.duration = duration; 19 | this.clear = clear; 20 | this.timer = timer; 21 | this.fns = [fn]; 22 | } 23 | 24 | /** 25 | * Calculate the time left for a given timer. 26 | * 27 | * @returns {Number} Time in milliseconds. 28 | * @api public 29 | */ 30 | Timer.prototype.remaining = function remaining() { 31 | return this.duration - this.taken(); 32 | }; 33 | 34 | /** 35 | * Calculate the amount of time it has taken since we've set the timer. 36 | * 37 | * @returns {Number} 38 | * @api public 39 | */ 40 | Timer.prototype.taken = function taken() { 41 | return +(new Date()) - this.start; 42 | }; 43 | 44 | /** 45 | * Custom wrappers for the various of clear{whatever} functions. We cannot 46 | * invoke them directly as this will cause thrown errors in Google Chrome with 47 | * an Illegal Invocation Error 48 | * 49 | * @see #2 50 | * @type {Function} 51 | * @api private 52 | */ 53 | function unsetTimeout(id) { clearTimeout(id); } 54 | function unsetInterval(id) { clearInterval(id); } 55 | function unsetImmediate(id) { clearImmediate(id); } 56 | 57 | /** 58 | * Simple timer management. 59 | * 60 | * @constructor 61 | * @param {Mixed} context Context of the callbacks that we execute. 62 | * @api public 63 | */ 64 | function Tick(context) { 65 | if (!(this instanceof Tick)) return new Tick(context); 66 | 67 | this.timers = {}; 68 | this.context = context || this; 69 | } 70 | 71 | /** 72 | * Return a function which will just iterate over all assigned callbacks and 73 | * optionally clear the timers from memory if needed. 74 | * 75 | * @param {String} name Name of the timer we need to execute. 76 | * @param {Boolean} clear Also clear from memory. 77 | * @returns {Function} 78 | * @api private 79 | */ 80 | Tick.prototype.tock = function ticktock(name, clear) { 81 | var tock = this; 82 | 83 | return function tickedtock() { 84 | if (!(name in tock.timers)) return; 85 | 86 | var timer = tock.timers[name] 87 | , fns = timer.fns.slice() 88 | , l = fns.length 89 | , i = 0; 90 | 91 | if (clear) tock.clear(name); 92 | else timer.start = +new Date(); 93 | 94 | for (; i < l; i++) { 95 | fns[i].call(tock.context); 96 | } 97 | }; 98 | }; 99 | 100 | /** 101 | * Add a new timeout. 102 | * 103 | * @param {String} name Name of the timer. 104 | * @param {Function} fn Completion callback. 105 | * @param {Mixed} time Duration of the timer. 106 | * @returns {Tick} 107 | * @api public 108 | */ 109 | Tick.prototype.setTimeout = function timeout(name, fn, time) { 110 | var tick = this 111 | , tock; 112 | 113 | if (tick.timers[name]) { 114 | tick.timers[name].fns.push(fn); 115 | return tick; 116 | } 117 | 118 | tock = ms(time); 119 | tick.timers[name] = new Timer( 120 | setTimeout(tick.tock(name, true), tock), 121 | unsetTimeout, 122 | tock, 123 | fn 124 | ); 125 | 126 | return tick; 127 | }; 128 | 129 | /** 130 | * Add a new interval. 131 | * 132 | * @param {String} name Name of the timer. 133 | * @param {Function} fn Completion callback. 134 | * @param {Mixed} time Interval of the timer. 135 | * @returns {Tick} 136 | * @api public 137 | */ 138 | Tick.prototype.setInterval = function interval(name, fn, time) { 139 | var tick = this 140 | , tock; 141 | 142 | if (tick.timers[name]) { 143 | tick.timers[name].fns.push(fn); 144 | return tick; 145 | } 146 | 147 | tock = ms(time); 148 | tick.timers[name] = new Timer( 149 | setInterval(tick.tock(name), tock), 150 | unsetInterval, 151 | tock, 152 | fn 153 | ); 154 | 155 | return tick; 156 | }; 157 | 158 | /** 159 | * Add a new setImmediate. 160 | * 161 | * @param {String} name Name of the timer. 162 | * @param {Function} fn Completion callback. 163 | * @returns {Tick} 164 | * @api public 165 | */ 166 | Tick.prototype.setImmediate = function immediate(name, fn) { 167 | var tick = this; 168 | 169 | if ('function' !== typeof setImmediate) return tick.setTimeout(name, fn, 0); 170 | 171 | if (tick.timers[name]) { 172 | tick.timers[name].fns.push(fn); 173 | return tick; 174 | } 175 | 176 | tick.timers[name] = new Timer( 177 | setImmediate(tick.tock(name, true)), 178 | unsetImmediate, 179 | 0, 180 | fn 181 | ); 182 | 183 | return tick; 184 | }; 185 | 186 | /** 187 | * Check if we have a timer set. 188 | * 189 | * @param {String} name 190 | * @returns {Boolean} 191 | * @api public 192 | */ 193 | Tick.prototype.active = function active(name) { 194 | return name in this.timers; 195 | }; 196 | 197 | /** 198 | * Properly clean up all timeout references. If no arguments are supplied we 199 | * will attempt to clear every single timer that is present. 200 | * 201 | * @param {Arguments} ..args.. The names of the timeouts we need to clear 202 | * @returns {Tick} 203 | * @api public 204 | */ 205 | Tick.prototype.clear = function clear() { 206 | var args = arguments.length ? arguments : [] 207 | , tick = this 208 | , timer, i, l; 209 | 210 | if (args.length === 1 && 'string' === typeof args[0]) { 211 | args = args[0].split(/[, ]+/); 212 | } 213 | 214 | if (!args.length) { 215 | for (timer in tick.timers) { 216 | if (has.call(tick.timers, timer)) args.push(timer); 217 | } 218 | } 219 | 220 | for (i = 0, l = args.length; i < l; i++) { 221 | timer = tick.timers[args[i]]; 222 | 223 | if (!timer) continue; 224 | timer.clear(timer.timer); 225 | 226 | timer.fns = timer.timer = timer.clear = null; 227 | delete tick.timers[args[i]]; 228 | } 229 | 230 | return tick; 231 | }; 232 | 233 | /** 234 | * Adjust a timeout or interval to a new duration. 235 | * 236 | * @returns {Tick} 237 | * @api public 238 | */ 239 | Tick.prototype.adjust = function adjust(name, time) { 240 | var interval 241 | , tick = this 242 | , tock = ms(time) 243 | , timer = tick.timers[name]; 244 | 245 | if (!timer) return tick; 246 | 247 | interval = timer.clear === unsetInterval; 248 | timer.clear(timer.timer); 249 | timer.start = +(new Date()); 250 | timer.duration = tock; 251 | timer.timer = (interval ? setInterval : setTimeout)(tick.tock(name, !interval), tock); 252 | 253 | return tick; 254 | }; 255 | 256 | /** 257 | * We will no longer use this module, prepare your self for global cleanups. 258 | * 259 | * @returns {Boolean} 260 | * @api public 261 | */ 262 | Tick.prototype.end = Tick.prototype.destroy = function end() { 263 | if (!this.context) return false; 264 | 265 | this.clear(); 266 | this.context = this.timers = null; 267 | 268 | return true; 269 | }; 270 | 271 | // 272 | // Expose the timer factory. 273 | // 274 | Tick.Timer = Timer; 275 | module.exports = Tick; 276 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | describe('ticktock', function () { 3 | 'use strict'; 4 | 5 | var assume = require('assume') 6 | , Tick = require('./') 7 | , Timer = Tick.Timer 8 | , tock; 9 | 10 | function fail() { 11 | throw new Error('I should never be executed'); 12 | } 13 | 14 | // 15 | // Make sure that we do not break on missing hasOwn checks. 16 | // 17 | Object.prototype.checkHasOwn = true; 18 | 19 | var context = { foo: 'bar' }; 20 | 21 | beforeEach(function () { 22 | tock = new Tick(); 23 | }); 24 | 25 | afterEach(function () { 26 | tock.end(); 27 | }); 28 | 29 | this.timeout(30000); 30 | 31 | it('is exported as a function', function () { 32 | assume(Tick).is.a('function'); 33 | }); 34 | 35 | it('can be constructed without new', function () { 36 | var tick = Tick(); 37 | 38 | assume(tick).is.instanceOf(Tick); 39 | }); 40 | 41 | describe('Timer', function () { 42 | describe('#taken', function () { 43 | it('calculates the time it has taken since first creation', function (next) { 44 | var timer = new Timer(undefined, undefined, 100); 45 | 46 | assume(timer.taken()).between(0, 1); 47 | 48 | setTimeout(function () { 49 | assume(timer.taken()).between(5, 15); 50 | next(); 51 | }, 10); 52 | }); 53 | }); 54 | 55 | describe('#remaining', function () { 56 | it('calculates the time remaining', function (next) { 57 | var timer = new Timer(undefined, undefined, 100); 58 | 59 | assume(timer.remaining()).between(99, 100); 60 | 61 | setTimeout(function () { 62 | assume(timer.remaining()).between(20, 35); 63 | next(); 64 | }, 70); 65 | }); 66 | }); 67 | }); 68 | 69 | describe('#tock', function () { 70 | it('returns a function', function () { 71 | assume(tock.tock('name')).is.a('function'); 72 | }); 73 | 74 | it('is save to execute', function () { 75 | tock.tock('name')(); 76 | }); 77 | 78 | it('can execute timers', function () { 79 | var called = false; 80 | 81 | tock.setTimeout('function', function () { 82 | called = true; 83 | }, 0); 84 | 85 | assume(called).is.false(); 86 | tock.tock('function', true)(); 87 | assume(called).is.true(); 88 | }); 89 | }); 90 | 91 | describe('#setInterval', function () { 92 | it('adds a setInterval', function (next) { 93 | var start = Date.now() 94 | , i = 0; 95 | 96 | tock.setInterval('test', function () { 97 | var taken = Date.now() - start; 98 | 99 | assume(this).equals(tock); 100 | 101 | if (i === 0) { 102 | assume(taken).is.above(5); 103 | assume(taken).is.below(25); 104 | } else { 105 | next(); 106 | } 107 | 108 | i++; 109 | }, 10); 110 | }); 111 | 112 | it('can be called with a custom context', function (next) { 113 | var tock = new Tick(context); 114 | 115 | tock.setInterval('test', function () { 116 | assume(this).deep.equals(context); 117 | tock.clear(); 118 | 119 | next(); 120 | }, 10); 121 | }); 122 | 123 | it('accepts strings for time', function (next) { 124 | var start = Date.now() 125 | , i = 0; 126 | 127 | tock.setInterval('test', function () { 128 | var taken = Date.now() - start; 129 | 130 | if (i === 0) { 131 | assume(taken).is.above(5); 132 | assume(taken).is.below(25); 133 | } else { 134 | next(); 135 | } 136 | 137 | i++; 138 | }, '10 ms'); 139 | }); 140 | 141 | it('run with the same timeout if a known name is provided', function (next) { 142 | var j = 0 143 | , i = 0; 144 | 145 | tock.setInterval('test', function () { 146 | j++; 147 | }, '100 ms'); 148 | 149 | setTimeout(function () { 150 | tock.setInterval('test', function () { 151 | i++; 152 | 153 | if (i === 10) { 154 | assume(j).equals(i); 155 | next(); 156 | } 157 | }, '100 ms'); 158 | }, 20); 159 | }); 160 | 161 | it('updates the start time of the timer instance', function (next) { 162 | tock.setInterval('timer', function () {}, 100); 163 | 164 | setTimeout(function () { 165 | assume(tock.timers.timer.remaining()).is.between(40, 65); 166 | next(); 167 | }, 250); 168 | }); 169 | }); 170 | 171 | describe('#setImmediate', function () { 172 | it('adds a setImmediate', function (next) { 173 | var start = Date.now(); 174 | 175 | tock.setImmediate('test', function () { 176 | var taken = Date.now() - start; 177 | 178 | assume(this).equals(tock); 179 | assume(taken).is.below(5); 180 | 181 | next(); 182 | }); 183 | }); 184 | 185 | it('can be called with a custom context', function (next) { 186 | var tock = new Tick(context); 187 | 188 | tock.setImmediate('test', function () { 189 | assume(this).deep.equals(context); 190 | next(); 191 | }); 192 | }); 193 | 194 | it('can be cleared', function (next) { 195 | var tock = new Tick(context); 196 | 197 | tock.setImmediate('test', function () { 198 | throw new Error('I should die'); 199 | }); 200 | 201 | tock.clear('test'); 202 | setTimeout(next, 100); 203 | }); 204 | 205 | it('clear it self after execution', function (next) { 206 | var tock = new Tick(context); 207 | 208 | tock.setImmediate('test', function () { 209 | tock.setImmediate('test', function () { 210 | next(); 211 | }); 212 | }); 213 | }); 214 | 215 | it('run with the same timeout if a known name is provided', function (next) { 216 | var ticks = []; 217 | 218 | tock.setImmediate('test', function () { 219 | ticks.push(1); 220 | }); 221 | 222 | tock.setImmediate('test', function () { 223 | ticks.push(2); 224 | assume(ticks.join()).equals('1,2'); 225 | next(); 226 | }); 227 | 228 | assume(tock.timers.test.fns).is.length(2); 229 | }); 230 | 231 | if ('function' === typeof setImmediate) 232 | it('fallsback to setTimeout if setImmediate does not exist', function (next) { 233 | setImmediate = null; 234 | tock.setImmediate('test', next); 235 | }); 236 | }); 237 | 238 | describe('#setTimeout', function () { 239 | it('adds a setTimeout', function (next) { 240 | var start = Date.now(); 241 | 242 | tock.setTimeout('test', function () { 243 | var taken = Date.now() - start; 244 | assume(this).deep.equals(tock); 245 | 246 | assume(taken).is.above(5); 247 | assume(taken).is.below(15); 248 | 249 | next(); 250 | }, 10); 251 | }); 252 | 253 | it('can be called with a custom context', function (next) { 254 | var tock = new Tick(context); 255 | 256 | tock.setTimeout('test', function () { 257 | assume(this).deep.equals(context); 258 | tock.clear(); 259 | 260 | next(); 261 | }, 10); 262 | }); 263 | 264 | it('accepts strings for time', function (next) { 265 | var start = Date.now(); 266 | 267 | tock.setTimeout('test', function () { 268 | var taken = Date.now() - start; 269 | 270 | assume(taken).is.above(5); 271 | assume(taken).is.below(15); 272 | 273 | next(); 274 | }, '10 ms'); 275 | }); 276 | 277 | it('run with the same timeout if a known name is provided', function (next) { 278 | var start = Date.now(); 279 | 280 | tock.setTimeout('test', function () { 281 | var taken = Date.now() - start; 282 | 283 | assume(taken).is.above(95); 284 | assume(taken).is.below(110); 285 | }, '100 ms'); 286 | 287 | setTimeout(function () { 288 | tock.setTimeout('test', function () { 289 | var taken = Date.now() - start; 290 | 291 | assume(taken).is.above(95); 292 | assume(taken).is.below(110); 293 | 294 | next(); 295 | }, '100 ms'); 296 | }, 20); 297 | }); 298 | }); 299 | 300 | describe('#clear', function () { 301 | it('clears multiple timeouts', function (next) { 302 | tock.setTimeout('timer', fail, '1 second'); 303 | tock.setTimeout('timer', fail, '1 second'); 304 | tock.setTimeout('timers', fail, '10 ms'); 305 | tock.setTimeout('timers', fail, 0); 306 | 307 | tock.clear('timer', 'timers'); 308 | 309 | setTimeout(function () { 310 | next(); 311 | }, 1010); 312 | }); 313 | 314 | it('splits the string if only one argument is supplied', function (next) { 315 | tock.setTimeout('timer', fail, '1 second'); 316 | tock.setTimeout('timer', fail, '1 second'); 317 | tock.setTimeout('timers', fail, '10 ms'); 318 | tock.setTimeout('timers', fail, 0); 319 | 320 | tock.clear('timer, timers, non-existing'); 321 | 322 | setTimeout(function () { 323 | next(); 324 | }, 1010); 325 | }); 326 | 327 | it('clears all if no arguments are supplied', function (next) { 328 | tock.setTimeout('timer', fail, '1 second'); 329 | tock.setTimeout('timer', fail, '1 second'); 330 | tock.setTimeout('timers', fail, '10 ms'); 331 | tock.setTimeout('timers', fail, 0); 332 | 333 | tock.clear(); 334 | 335 | setTimeout(function () { 336 | next(); 337 | }, 1010); 338 | }); 339 | }); 340 | 341 | describe('#active', function () { 342 | it('returns true if a timer is defined', function () { 343 | assume(tock.active('foo')).is.false(); 344 | 345 | tock.setTimeout('foo', function () {}); 346 | assume(tock.active('foo')).is.true(); 347 | 348 | tock.clear(); 349 | assume(tock.active('foo')).is.false(); 350 | }); 351 | }); 352 | 353 | describe('#adjust', function () { 354 | it('adjusts nothing for unknown timers', function () { 355 | tock.adjust('foo', '1 second'); 356 | }); 357 | 358 | it('adjusts the timeout', function (next) { 359 | tock.setTimeout('timer', fail, '10 ms'); 360 | tock.adjust('timer', '1 second'); 361 | 362 | setTimeout(function () { 363 | tock.clear(); 364 | next(); 365 | }, 500); 366 | }); 367 | 368 | it('also adjusts the timer instance start time', function (next) { 369 | tock.setTimeout('timer', fail, '100 ms'); 370 | 371 | setTimeout(function () { 372 | assume(tock.timers.timer.remaining()).is.between(80, 95); 373 | assume(tock.timers.timer.taken()).is.between(5, 15); 374 | 375 | tock.adjust('timer', '1 second'); 376 | 377 | assume(tock.timers.timer.taken()).is.between(0, 1); 378 | assume(tock.timers.timer.remaining()).is.between(999, 1000); 379 | 380 | tock.clear(); 381 | next(); 382 | }, 10); 383 | }); 384 | 385 | it('adjusts the interval', function (next) { 386 | var start = Date.now() 387 | , counter = 0 388 | , elapsed; 389 | 390 | tock.setInterval('foo', function () { 391 | elapsed = Date.now() - start; 392 | 393 | if (++counter === 1) { 394 | assume(elapsed).is.below(30); 395 | tock.adjust('foo', '100 ms'); 396 | } else if (counter === 3) { 397 | assume(elapsed).is.between(200, 250); 398 | tock.clear(); 399 | next(); 400 | } 401 | }); 402 | }); 403 | }); 404 | 405 | describe('#end', function () { 406 | it('returns true when its cleared the first time', function () { 407 | assume(tock.end()).is.true(); 408 | }); 409 | 410 | it('returns false when already cleared', function () { 411 | assume(tock.end()).is.true(); 412 | assume(tock.end()).is.false(); 413 | }); 414 | }); 415 | }); 416 | --------------------------------------------------------------------------------