pax_global_header00006660000000000000000000000064131530272010014504gustar00rootroot0000000000000052 comment=59fc0ad0824d76d965ce54bae6d594c27f5e6681 d3-timer-1.0.7/000077500000000000000000000000001315302720100131355ustar00rootroot00000000000000d3-timer-1.0.7/.eslintrc000066400000000000000000000001411315302720100147550ustar00rootroot00000000000000parserOptions: sourceType: module env: browser: true extends: "eslint:recommended" d3-timer-1.0.7/.gitignore000066400000000000000000000001001315302720100151140ustar00rootroot00000000000000*.sublime-workspace .DS_Store build/ node_modules npm-debug.log d3-timer-1.0.7/.npmignore000066400000000000000000000000361315302720100151330ustar00rootroot00000000000000*.sublime-* build/*.zip test/ d3-timer-1.0.7/LICENSE000066400000000000000000000027031315302720100141440ustar00rootroot00000000000000Copyright 2010-2016 Mike Bostock All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. d3-timer-1.0.7/README.md000066400000000000000000000135651315302720100144260ustar00rootroot00000000000000# d3-timer This module provides an efficient queue capable of managing thousands of concurrent animations, while guaranteeing consistent, synchronized timing with concurrent or staged animations. Internally, it uses [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) for fluid animation (if available), switching to [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout) for delays longer than 24ms. ## Installing If you use NPM, `npm install d3-timer`. Otherwise, download the [latest release](https://github.com/d3/d3-timer/releases/latest). You can also load directly from [d3js.org](https://d3js.org), either as a [standalone library](https://d3js.org/d3-timer.v1.min.js) or as part of [D3 4.0](https://github.com/d3/d3). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported: ```html ``` [Try d3-timer in your browser.](https://tonicdev.com/npm/d3-timer) ## API Reference # d3.now() [<>](https://github.com/d3/d3-timer/blob/master/src/timer.js#L15 "Source") Returns the current time as defined by [performance.now](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) if available, and [Date.now](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now) if not. The current time is updated at the start of a frame; it is thus consistent during the frame, and any timers scheduled during the same frame will be synchronized. If this method is called outside of a frame, such as in response to a user event, the current time is calculated and then fixed until the next frame, again ensuring consistent timing during event handling. # d3.timer(callback[, delay[, time]]) [<>](https://github.com/d3/d3-timer/blob/master/src/timer.js#L52 "Source") Schedules a new timer, invoking the specified *callback* repeatedly until the timer is [stopped](#timer_stop). An optional numeric *delay* in milliseconds may be specified to invoke the given *callback* after a delay; if *delay* is not specified, it defaults to zero. The delay is relative to the specified *time* in milliseconds; if *time* is not specified, it defaults to [now](#now). The *callback* is passed the (apparent) *elapsed* time since the timer became active. For example: ```js var t = d3.timer(function(elapsed) { console.log(elapsed); if (elapsed > 200) t.stop(); }, 150); ``` This produces roughly the following console output: ``` 3 25 48 65 85 106 125 146 167 189 209 ``` (The exact values may vary depending on your JavaScript runtime and what else your computer is doing.) Note that the first *elapsed* time is 3ms: this is the elapsed time since the timer started, not since the timer was scheduled. Here the timer started 150ms after it was scheduled due to the specified delay. The apparent *elapsed* time may be less than the true *elapsed* time if the page is backgrounded and [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) is paused; in the background, apparent time is frozen. If [timer](#timer) is called within the callback of another timer, the new timer callback (if eligible as determined by the specified *delay* and *time*) will be invoked immediately at the end of the current frame, rather than waiting until the next frame. Within a frame, timer callbacks are guaranteed to be invoked in the order they were scheduled, regardless of their start time. # timer.restart(callback[, delay[, time]]) [<>](https://github.com/d3/d3-timer/blob/master/src/timer.js#L31 "Source") Restart a timer with the specified *callback* and optional *delay* and *time*. This is equivalent to stopping this timer and creating a new timer with the specified arguments, although this timer retains the original invocation priority. # timer.stop() [<>](https://github.com/d3/d3-timer/blob/master/src/timer.js#L43 "Source") Stops this timer, preventing subsequent callbacks. This method has no effect if the timer has already stopped. # d3.timerFlush() [<>](https://github.com/d3/d3-timer/blob/master/src/timer.js#L58 "Source") Immediately invoke any eligible timer callbacks. Note that zero-delay timers are normally first executed after one frame (~17ms). This can cause a brief flicker because the browser renders the page twice: once at the end of the first event loop, then again immediately on the first timer callback. By flushing the timer queue at the end of the first event loop, you can run any zero-delay timers immediately and avoid the flicker. # d3.timeout(callback[, delay[, time]]) [<>](https://github.com/d3/d3-timer/blob/master/src/timeout.js "Source") Like [timer](#timer), except the timer automatically [stops](#timer_stop) on its first callback. A suitable replacement for [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout) that is guaranteed to not run in the background. The *callback* is passed the elapsed time. # d3.interval(callback[, delay[, time]]) [<>](https://github.com/d3/d3-timer/blob/master/src/interval.js "Source") Like [timer](#timer), except the *callback* is invoked only every *delay* milliseconds; if *delay* is not specified, this is equivalent to [timer](#timer). A suitable replacement for [setInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval) that is guaranteed to not run in the background. The *callback* is passed the elapsed time. d3-timer-1.0.7/d3-timer.sublime-project000066400000000000000000000002711315302720100176070ustar00rootroot00000000000000{ "folders": [ { "path": ".", "file_exclude_patterns": [ "*.sublime-workspace" ], "folder_exclude_patterns": [ "build" ] } ] } d3-timer-1.0.7/index.js000066400000000000000000000002541315302720100146030ustar00rootroot00000000000000export { now, timer, timerFlush } from "./src/timer"; export { default as timeout } from "./src/timeout"; export { default as interval } from "./src/interval"; d3-timer-1.0.7/package.json000066400000000000000000000027111315302720100154240ustar00rootroot00000000000000{ "name": "d3-timer", "version": "1.0.7", "description": "An efficient queue capable of managing thousands of concurrent animations.", "keywords": [ "d3", "d3-module", "timer", "transition", "animation", "requestAnimationFrame", "setTimeout", "setInterval" ], "homepage": "https://d3js.org/d3-timer/", "license": "BSD-3-Clause", "author": { "name": "Mike Bostock", "url": "http://bost.ocks.org/mike" }, "main": "build/d3-timer.js", "module": "index", "jsnext:main": "index", "repository": { "type": "git", "url": "https://github.com/d3/d3-timer.git" }, "scripts": { "pretest": "rm -rf build && mkdir build && rollup -c --banner \"$(preamble)\"", "test": "tape 'test/**/*-test.js' && eslint index.js src", "prepublishOnly": "npm run test && uglifyjs -b beautify=false,preamble=\"'$(preamble)'\" build/d3-timer.js -c -m -o build/d3-timer.min.js", "postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3-timer/build/d3-timer.js d3-timer.v1.js && cp ../d3-timer/build/d3-timer.min.js d3-timer.v1.min.js && git add d3-timer.v1.js d3-timer.v1.min.js && git commit -m \"d3-timer ${npm_package_version}\" && git push && cd - && zip -j build/d3-timer.zip -- LICENSE README.md build/d3-timer.js build/d3-timer.min.js" }, "devDependencies": { "eslint": "4", "package-preamble": "0.1", "rollup": "0.49", "tape": "4", "uglify-js": "3" } } d3-timer-1.0.7/rollup.config.js000066400000000000000000000002111315302720100162460ustar00rootroot00000000000000export default { input: "index", output: { extend: true, file: "build/d3-timer.js", format: "umd", name: "d3" } }; d3-timer-1.0.7/src/000077500000000000000000000000001315302720100137245ustar00rootroot00000000000000d3-timer-1.0.7/src/interval.js000066400000000000000000000006201315302720100161040ustar00rootroot00000000000000import {Timer, now} from "./timer"; export default function(callback, delay, time) { var t = new Timer, total = delay; if (delay == null) return t.restart(callback, delay, time), t; delay = +delay, time = time == null ? now() : +time; t.restart(function tick(elapsed) { elapsed += total; t.restart(tick, total += delay, time); callback(elapsed); }, delay, time); return t; } d3-timer-1.0.7/src/timeout.js000066400000000000000000000003721315302720100157520ustar00rootroot00000000000000import {Timer} from "./timer"; export default function(callback, delay, time) { var t = new Timer; delay = delay == null ? 0 : +delay; t.restart(function(elapsed) { t.stop(); callback(elapsed + delay); }, delay, time); return t; } d3-timer-1.0.7/src/timer.js000066400000000000000000000055311315302720100154060ustar00rootroot00000000000000var frame = 0, // is an animation frame pending? timeout = 0, // is a timeout pending? interval = 0, // are any timers active? pokeDelay = 1000, // how frequently we check for clock skew taskHead, taskTail, clockLast = 0, clockNow = 0, clockSkew = 0, clock = typeof performance === "object" && performance.now ? performance : Date, setFrame = typeof window === "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(f) { setTimeout(f, 17); }; export function now() { return clockNow || (setFrame(clearNow), clockNow = clock.now() + clockSkew); } function clearNow() { clockNow = 0; } export function Timer() { this._call = this._time = this._next = null; } Timer.prototype = timer.prototype = { constructor: Timer, restart: function(callback, delay, time) { if (typeof callback !== "function") throw new TypeError("callback is not a function"); time = (time == null ? now() : +time) + (delay == null ? 0 : +delay); if (!this._next && taskTail !== this) { if (taskTail) taskTail._next = this; else taskHead = this; taskTail = this; } this._call = callback; this._time = time; sleep(); }, stop: function() { if (this._call) { this._call = null; this._time = Infinity; sleep(); } } }; export function timer(callback, delay, time) { var t = new Timer; t.restart(callback, delay, time); return t; } export function timerFlush() { now(); // Get the current time, if not already set. ++frame; // Pretend we’ve set an alarm, if we haven’t already. var t = taskHead, e; while (t) { if ((e = clockNow - t._time) >= 0) t._call.call(null, e); t = t._next; } --frame; } function wake() { clockNow = (clockLast = clock.now()) + clockSkew; frame = timeout = 0; try { timerFlush(); } finally { frame = 0; nap(); clockNow = 0; } } function poke() { var now = clock.now(), delay = now - clockLast; if (delay > pokeDelay) clockSkew -= delay, clockLast = now; } function nap() { var t0, t1 = taskHead, t2, time = Infinity; while (t1) { if (t1._call) { if (time > t1._time) time = t1._time; t0 = t1, t1 = t1._next; } else { t2 = t1._next, t1._next = null; t1 = t0 ? t0._next = t2 : taskHead = t2; } } taskTail = t0; sleep(time); } function sleep(time) { if (frame) return; // Soonest alarm already set, or will be. if (timeout) timeout = clearTimeout(timeout); var delay = time - clockNow; // Strictly less than if we recomputed clockNow. if (delay > 24) { if (time < Infinity) timeout = setTimeout(wake, time - clock.now() - clockSkew); if (interval) interval = clearInterval(interval); } else { if (!interval) clockLast = clock.now(), interval = setInterval(poke, pokeDelay); frame = 1, setFrame(wake); } } d3-timer-1.0.7/test/000077500000000000000000000000001315302720100141145ustar00rootroot00000000000000d3-timer-1.0.7/test/end.js000066400000000000000000000003571315302720100152250ustar00rootroot00000000000000// Some tests need a trailing frame after timers are stopped to cleanup the // queue and clear the alarm. Let that finish before starting a new test. module.exports = function(test) { setTimeout(function() { test.end(); }, 24); }; d3-timer-1.0.7/test/inRange.js000066400000000000000000000004531315302720100160370ustar00rootroot00000000000000var tape = require("tape"); tape.Test.prototype.inRange = function(actual, expectedMin, expectedMax) { this._assert(expectedMin <= actual && actual <= expectedMax, { message: "should be in range", operator: "inRange", actual: actual, expected: [expectedMin, expectedMax] }); }; d3-timer-1.0.7/test/interval-test.js000066400000000000000000000043031315302720100172530ustar00rootroot00000000000000var tape = require("tape"), timer = require("../"), end = require("./end"); require("./inRange"); // It’s difficult to test the timing behavior reliably, since there can be small // hiccups that cause a timer to be delayed. So we test only the mean rate. tape("interval(callback) invokes the callback about every 17ms", function(test) { var then = timer.now(), count = 0; var t = timer.interval(function() { if (++count > 10) { t.stop(); test.inRange(timer.now() - then, (17 - 5) * count, (17 + 5) * count); end(test); } }); }); tape("interval(callback) invokes the callback until the timer is stopped", function(test) { var count = 0; var t = timer.interval(function() { if (++count > 2) { t.stop(); end(test); } }); }); tape("interval(callback, delay) invokes the callback about every delay milliseconds", function(test) { var then = timer.now(), delay = 50, nows = [then]; var t = timer.interval(function() { if (nows.push(timer.now()) > 10) { t.stop(); nows.forEach(function(now, i) { test.inRange(now - then, delay * i - 10, delay * i + 10); }); end(test); } }, delay); }); tape("interval(callback, delay, time) invokes the callback repeatedly after the specified delay relative to the given time", function(test) { var then = timer.now() + 50, delay = 50; var t = timer.interval(function(elapsed) { test.inRange(timer.now() - then, delay - 10, delay + 10); t.stop(); end(test); }, delay, then); }); tape("interval(callback) uses the global context for the callback", function(test) { var t = timer.interval(function() { test.equal(this, global); t.stop(); end(test); }); }); tape("interval(callback) passes the callback the elapsed time", function(test) { var then = timer.now(), count = 0; var t = timer.interval(function(elapsed) { test.equal(elapsed, timer.now() - then); t.stop(); end(test); }, 100); }); tape("interval(callback) returns a timer", function(test) { var count = 0; var t = timer.interval(function() { ++count; }); test.equal(t instanceof timer.timer, true); t.stop(); setTimeout(function() { test.equal(count, 0); end(test); }, 100); }); d3-timer-1.0.7/test/now-test.js000066400000000000000000000010071315302720100162300ustar00rootroot00000000000000var tape = require("tape"), timer = require("../"), end = require("./end"); require("./inRange"); tape("now() returns the same time when called repeatedly", function(test) { var now = timer.now(); test.ok(now > 0); test.equal(timer.now(), now); end(test); }); tape("now() returns a different time when called after a timeout", function(test) { var then = timer.now(); test.ok(then > 0); setTimeout(function() { test.inRange(timer.now() - then, 50 - 5, 50 + 5); end(test); }, 50); }); d3-timer-1.0.7/test/timeout-test.js000066400000000000000000000027621315302720100171240ustar00rootroot00000000000000var tape = require("tape"), timer = require("../"), end = require("./end"); require("./inRange"); tape("timeout(callback) invokes the callback once", function(test) { var count = 0; timer.timeout(function() { test.equal(++count, 1); end(test); }); }); tape("timeout(callback, delay) invokes the callback once after the specified delay", function(test) { var then = timer.now(), delay = 50; timer.timeout(function(elapsed) { test.inRange(timer.now() - then, delay - 10, delay + 10); end(test); }, delay); }); tape("timeout(callback, delay, time) invokes the callback once after the specified delay relative to the given time", function(test) { var then = timer.now() + 50, delay = 50; timer.timeout(function(elapsed) { test.inRange(timer.now() - then, delay - 10, delay + 10); end(test); }, delay, then); }); tape("timeout(callback) uses the global context for the callback", function(test) { timer.timeout(function() { test.equal(this, global); end(test); }); }); tape("timeout(callback) passes the callback the elapsed time", function(test) { var then = timer.now(), count = 0; timer.timeout(function(elapsed) { test.equal(elapsed, timer.now() - then); end(test); }); }); tape("timeout(callback) returns a timer", function(test) { var count = 0; var t = timer.timeout(function() { ++count; }); test.equal(t instanceof timer.timer, true); t.stop(); setTimeout(function() { test.equal(count, 0); end(test); }, 100); }); d3-timer-1.0.7/test/timer-test.js000066400000000000000000000324451315302720100165570ustar00rootroot00000000000000var tape = require("tape"), timer = require("../"), end = require("./end"); require("./inRange"); tape("timer(callback) returns an instanceof timer", function(test) { var t = timer.timer(function() {}); test.equal(t instanceof timer.timer, true); test.equal(t.constructor.name, "Timer"); t.stop(); end(test); }); tape("timer(callback) verifies that callback is a function", function(test) { test.throws(function() { timer.timer(); }, TypeError); test.throws(function() { timer.timer("42"); }, TypeError); test.throws(function() { timer.timer(null); }, TypeError); test.end(); }); // It’s difficult to test the timing behavior reliably, since there can be small // hiccups that cause a timer to be delayed. So we test only the mean rate. tape("timer(callback) invokes the callback about every 17ms", function(test) { var then = timer.now(), count = 0; var t = timer.timer(function() { if (++count > 10) { t.stop(); test.inRange(timer.now() - then, (17 - 5) * count, (17 + 5) * count); end(test); } }); }); tape("timer(callback) invokes the callback until the timer is stopped", function(test) { var count = 0; var t = timer.timer(function() { if (++count > 2) { t.stop(); end(test); } }); }); tape("timer(callback) uses the global context for the callback", function(test) { var t = timer.timer(function() { test.equal(this, global); t.stop(); end(test); }); }); tape("timer(callback) passes the callback the elapsed time", function(test) { var then = timer.now(), count = 0; var t = timer.timer(function(elapsed) { ++count; test.equal(elapsed, timer.now() - then); if (count > 10) { t.stop(); end(test); } }); }); tape("timer(callback, delay) first invokes the callback after the specified delay", function(test) { var then = timer.now(), delay = 150; var t = timer.timer(function() { t.stop(); test.inRange(timer.now() - then, delay - 10, delay + 10); end(test); }, delay); }); tape("timer(callback, delay) computes the elapsed time relative to the delay", function(test) { var delay = 150; var t = timer.timer(function(elapsed) { t.stop(); test.inRange(elapsed, 0, 10); end(test); }, delay); }); tape("timer(callback, delay, time) computes the effective delay relative to the specified time", function(test) { var delay = 150, skew = 200; var t = timer.timer(function(elapsed) { t.stop(); test.inRange(elapsed, skew - delay + 17 - 10, skew - delay + 17 + 10); end(test); }, delay, timer.now() - skew); }); tape("timer(callback) invokes callbacks in scheduling order during synchronous flush", function(test) { var results = []; var t0 = timer.timer(function() { results.push(1); t0.stop(); }); var t1 = timer.timer(function() { results.push(2); t1.stop(); }); var t2 = timer.timer(function() { results.push(3); t2.stop(); }); timer.timerFlush(); test.deepEqual(results, [1, 2, 3]); end(test); }); tape("timer(callback) invokes callbacks in scheduling order during asynchronous flush", function(test) { var results = []; var t0 = timer.timer(function() { results.push(1); t0.stop(); }); var t1 = timer.timer(function() { results.push(2); t1.stop(); }); var t2 = timer.timer(function() { results.push(3); t2.stop(); }); var t3 = timer.timer(function() { t3.stop(); test.deepEqual(results, [1, 2, 3]); end(test); }); }); // Even though these timers have different delays, they are still called back in // scheduling order when they are simultaneously active. tape("timer(callback, delay) invokes callbacks in scheduling order during asynchronous flush", function(test) { var then = timer.now(), results; var t0 = timer.timer(function() { results = [1]; t0.stop(); }, 60, then); var t1 = timer.timer(function() { if (results) results.push(2), t1.stop(); }, 40, then); var t2 = timer.timer(function() { if (results) results.push(3), t2.stop(); }, 80, then); var t3 = timer.timer(function() { t3.stop(); test.deepEqual(results, [1, 2, 3]); end(test); }, 100, then); }); tape("timer(callback) within a frame invokes the callback at the end of the same frame", function(test) { var then = timer.now(); var t0 = timer.timer(function(elapsed, now) { var delay = timer.now() - then; var t1 = timer.timer(function(elapsed2, now2) { t1.stop(); test.equal(elapsed2, 0); test.equal(now2, now); test.inRange(timer.now() - then, delay, delay + 3); end(test); }, 0, now); t0.stop(); }); }); // Note: assumes that Node doesn’t support requestAnimationFrame, falling back to setTimeout. tape("timer(callback, delay) within a timerFlush() does not request duplicate frames", function(test) { var setTimeout0 = setTimeout, frames = 0; // This requests a frame, too, so do it before the test starts. timer.now(); setTimeout = function() { ++frames; return setTimeout0.apply(this, arguments); }; var t0 = timer.timer(function(elapsed) { // 2. The first timer is invoked synchronously by timerFlush, so only the // first frame—when this timer was created—has been requested. test.equal(frames, 1); t0.stop(); // 3. This timer was stopped during flush, so it doesn’t request a frame. test.equal(frames, 1); var t1 = timer.timer(function() { // 6. Still only one frame has been requested so far: the second timer has // a <17ms delay, and so was called back during the first frame requested // by the first timer on creation. If the second timer had a longer delay, // it might need another frame (or timeout) before invocation. test.equal(frames, 1); t1.stop(); // 7. Stopping the second timer doesn’t immediately request a frame since // we’re now within an implicit flush (initiated by this timer). test.equal(frames, 1); setTimeout0(function() { // 8. Since the timer queue was empty when we stopped the second timer, // no additional frame was requested after the timers were flushed. test.equal(frames, 1); setTimeout = setTimeout0; end(test); }, 50); }, 1); // 4. Creating a second timer during flush also doesn’t immediately request // a frame; the request would happen AFTER all the timers are called back, // and we still have the request active from when the first timer was // created, since the first timer is invoked synchronously. test.equal(frames, 1); }); // 1. Creating the first timer requests the first frame. test.equal(frames, 1); timer.timerFlush(); // 5. Still only one frame active! test.equal(frames, 1); }); // Note: assumes that Node doesn’t support requestAnimationFrame, falling back to setTimeout. tape("timer(callback) switches to setTimeout for long delays", function(test) { var setTimeout0 = setTimeout, frames = 0, timeouts = 0; // This requests a frame, too, so do it before the test starts. timer.now(); setTimeout = function(callback, delay) { delay === 17 ? ++frames : ++timeouts; return setTimeout0.apply(this, arguments); }; var t0 = timer.timer(function() { // 2. The first timer had a delay >24ms, so after the first scheduling // frame, we used a longer timeout to wake up. test.equal(frames, 1); test.equal(timeouts, 1); t0.stop(); // 3. Stopping a timer during flush doesn’t request a new frame. test.equal(frames, 1); test.equal(timeouts, 1); var t1 = timer.timer(function() { // 5. The second timer had a short delay, so it’s not immediately invoked // during the same tick as the first timer; it gets a new frame. test.equal(frames, 2); test.equal(timeouts, 1); t1.stop(); // 6. Stopping a timer during flush doesn’t request a new frame. test.equal(frames, 2); test.equal(timeouts, 1); setTimeout0(function() { // 7. Since the timer queue was empty when we stopped the second timer, // no additional frame was requested after the timers were flushed. test.equal(frames, 2); test.equal(timeouts, 1); setTimeout = setTimeout0; end(test); }, 50); }, 1); // 4. Scheduling a new timer during flush doesn’t request a new frame; // that happens after all the timers have been invoked. test.equal(frames, 1); test.equal(timeouts, 1); }, 100); // 1. Creating the first timer requests the first frame. Even though the timer // has a long delay, we always use a frame to consolidate timer creation for // multiple timers. That way, if you schedule a bunch of timers with different // delays, we don’t thrash timeouts. test.equal(frames, 1); test.equal(timeouts, 0); }); tape("timer.stop() immediately stops the timer", function(test) { var count = 0; var t = timer.timer(function() { ++count; }); setTimeout(function() { t.stop(); test.equal(count, 1); end(test); }, 24); }); tape("timer.stop() recomputes the new wake time after one frame", function(test) { var setTimeout0 = setTimeout, delays = []; // This requests a frame, too, so do it before the test starts. timer.now(); setTimeout = function(callback, delay) { delays.push(delay); return setTimeout0.apply(this, arguments); }; var t0 = timer.timer(function() {}, 1000); var t1 = timer.timer(function() {}, 500); setTimeout0(function() { // 1. The two timers are scheduling in the first frame, and then the new // wake time is computed based on minimum relative time of active timers. test.equal(delays.length, 2); test.equal(delays[0], 17); test.inRange(delays[1], 500 - 17 - 10, 500 - 17 + 10); t1.stop(); // 2. The second timer (with the smaller delay) was stopped, but the new // wake time isn’t computed until the next frame, since we stopped the timer // outside of a flush. test.equal(delays.length, 3); test.equal(delays[2], 17); setTimeout0(function() { // 3. The alarm was reset to wake for the long-delay timer. test.equal(delays.length, 4); test.inRange(delays[3], 1000 - 100 - 17 * 1.5 - 10, 1000 - 100 - 17 * 1.5 + 10); t0.stop(); setTimeout0(function() { // 4. The alarm was cleared after the long-delay timer was cancelled. test.equal(delays.length, 5); test.equal(delays[4], 17); setTimeout = setTimeout0; end(test); }, 100); }, 100); }, 100); }); tape("timer.restart(callback) verifies that callback is a function", function(test) { var t = timer.timer(function() {}); test.throws(function() { t.restart(); }, TypeError); test.throws(function() { t.restart(null); }, TypeError); test.throws(function() { t.restart("42"); }, TypeError); t.stop(); end(test); }); tape("timer.restart(callback) implicitly uses zero delay and the current time", function(test) { var t = timer.timer(function() {}, 1000); t.restart(function(elapsed) { test.inRange(elapsed, 17 - 10, 17 + 10); t.stop(); end(test); }); }); tape("timer.restart(callback, delay, time) recomputes the new wake time after one frame", function(test) { var then = timer.now(), setTimeout0 = setTimeout, delays = []; setTimeout = function(callback, delay) { delays.push(delay); return setTimeout0.apply(this, arguments); }; var t = timer.timer(function() {}, 500, then); setTimeout0(function() { // 1. The timer is scheduled in the first frame, and then the new wake time // is computed based on its relative time. test.equal(delays.length, 2); test.equal(delays[0], 17); test.inRange(delays[1], 500 - 17 - 10, 500 - 17 + 10); t.restart(function() {}, 1000, then); // 2. The timer was delayed, but the new wake time isn’t computed until the // next frame, since we restarted the timer outside of a flush. test.equal(delays.length, 3); test.equal(delays[2], 17); setTimeout0(function() { // 3. The alarm was reset to wake for the longer delay. test.equal(delays.length, 4); test.inRange(delays[3], 1000 - 100 - 17 * 1.5 - 10, 1000 - 100 - 17 * 1.5 + 10); t.stop(); setTimeout0(function() { // 4. The alarm was cleared after the timer was cancelled. test.equal(delays.length, 5); test.equal(delays[4], 17); setTimeout = setTimeout0; end(test); }, 100); }, 100); }, 100); }); tape("timer.stop() immediately followed by timer.restart() doesn’t cause an infinite loop", function(test) { var t = timer.timer(function() {}), last; t.stop(); t.restart(function(elapsed) { if (!last) return last = elapsed; if (elapsed === last) test.fail("repeated invocation"); t.stop(); test.end(); }); }); tape("timer.stop() immediately followed by timer.restart() doesn’t cause an infinite loop (2)", function(test) { var t0 = timer.timer(function() {}), t1 = timer.timer(function() {}), last; t0.stop(); t0.restart(function(elapsed) { if (!last) return last = elapsed; if (elapsed === last) test.fail("repeated invocation"); t0.stop(); test.end(); }); t1.stop(); }); tape("timer.stop() clears the internal _next field after a timeout", function(test) { var t0 = timer.timer(function() {}), t1 = timer.timer(function() {}); t0.stop(); setTimeout(function() { test.equal(!t0._next, true); t1.stop(); test.end(); }, 100); }); d3-timer-1.0.7/test/timerFlush-test.js000066400000000000000000000020131315302720100175450ustar00rootroot00000000000000var tape = require("tape"), timer = require("../"), end = require("./end"); tape("timerFlush() immediately invokes any eligible timers", function(test) { var count = 0; var t = timer.timer(function() { ++count; t.stop(); }); timer.timerFlush(); timer.timerFlush(); test.equal(count, 1); end(test); }); tape("timerFlush() within timerFlush() still executes all eligible timers", function(test) { var count = 0; var t = timer.timer(function() { if (++count >= 3) t.stop(); timer.timerFlush(); }); timer.timerFlush(); test.equal(count, 3); end(test); }); tape("timerFlush() observes the current time", function(test) { var start = timer.now(), foos = 0, bars = 0, bazs = 0; var foo = timer.timer(function() { ++foos; foo.stop(); }, 0, start + 1); var bar = timer.timer(function() { ++bars; bar.stop(); }, 0, start); var baz = timer.timer(function() { ++bazs; baz.stop(); }, 0, start - 1); timer.timerFlush(); test.equal(foos, 0); test.equal(bars, 1); test.equal(bazs, 1); end(test); });