pax_global_header 0000666 0000000 0000000 00000000064 13153027201 0014504 g ustar 00root root 0000000 0000000 52 comment=59fc0ad0824d76d965ce54bae6d594c27f5e6681
d3-timer-1.0.7/ 0000775 0000000 0000000 00000000000 13153027201 0013135 5 ustar 00root root 0000000 0000000 d3-timer-1.0.7/.eslintrc 0000664 0000000 0000000 00000000141 13153027201 0014755 0 ustar 00root root 0000000 0000000 parserOptions:
sourceType: module
env:
browser: true
extends:
"eslint:recommended"
d3-timer-1.0.7/.gitignore 0000664 0000000 0000000 00000000100 13153027201 0015114 0 ustar 00root root 0000000 0000000 *.sublime-workspace
.DS_Store
build/
node_modules
npm-debug.log
d3-timer-1.0.7/.npmignore 0000664 0000000 0000000 00000000036 13153027201 0015133 0 ustar 00root root 0000000 0000000 *.sublime-*
build/*.zip
test/
d3-timer-1.0.7/LICENSE 0000664 0000000 0000000 00000002703 13153027201 0014144 0 ustar 00root root 0000000 0000000 Copyright 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.md 0000664 0000000 0000000 00000013565 13153027201 0014426 0 ustar 00root root 0000000 0000000 # 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-project 0000664 0000000 0000000 00000000271 13153027201 0017607 0 ustar 00root root 0000000 0000000 {
"folders": [
{
"path": ".",
"file_exclude_patterns": [
"*.sublime-workspace"
],
"folder_exclude_patterns": [
"build"
]
}
]
}
d3-timer-1.0.7/index.js 0000664 0000000 0000000 00000000254 13153027201 0014603 0 ustar 00root root 0000000 0000000 export {
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.json 0000664 0000000 0000000 00000002711 13153027201 0015424 0 ustar 00root root 0000000 0000000 {
"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.js 0000664 0000000 0000000 00000000211 13153027201 0016246 0 ustar 00root root 0000000 0000000 export default {
input: "index",
output: {
extend: true,
file: "build/d3-timer.js",
format: "umd",
name: "d3"
}
};
d3-timer-1.0.7/src/ 0000775 0000000 0000000 00000000000 13153027201 0013724 5 ustar 00root root 0000000 0000000 d3-timer-1.0.7/src/interval.js 0000664 0000000 0000000 00000000620 13153027201 0016104 0 ustar 00root root 0000000 0000000 import {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.js 0000664 0000000 0000000 00000000372 13153027201 0015752 0 ustar 00root root 0000000 0000000 import {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.js 0000664 0000000 0000000 00000005531 13153027201 0015406 0 ustar 00root root 0000000 0000000 var 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/ 0000775 0000000 0000000 00000000000 13153027201 0014114 5 ustar 00root root 0000000 0000000 d3-timer-1.0.7/test/end.js 0000664 0000000 0000000 00000000357 13153027201 0015225 0 ustar 00root root 0000000 0000000 // 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.js 0000664 0000000 0000000 00000000453 13153027201 0016037 0 ustar 00root root 0000000 0000000 var 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.js 0000664 0000000 0000000 00000004303 13153027201 0017253 0 ustar 00root root 0000000 0000000 var 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.js 0000664 0000000 0000000 00000001007 13153027201 0016230 0 ustar 00root root 0000000 0000000 var 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.js 0000664 0000000 0000000 00000002762 13153027201 0017124 0 ustar 00root root 0000000 0000000 var 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.js 0000664 0000000 0000000 00000032445 13153027201 0016557 0 ustar 00root root 0000000 0000000 var 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.js 0000664 0000000 0000000 00000002013 13153027201 0017545 0 ustar 00root root 0000000 0000000 var 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);
});