Comparing version 0.0.5 to 0.0.6
@@ -1,11 +0,4 @@ | ||
import { | ||
export { | ||
timer, | ||
timerReplace, | ||
timerFlush | ||
} from "./src/timer"; | ||
export { | ||
timer, | ||
timerReplace, | ||
timerFlush | ||
}; |
{ | ||
"name": "d3-timer", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "An efficient queue capable of managing thousands of concurrent animations.", | ||
@@ -20,3 +20,3 @@ "keywords": [ | ||
}, | ||
"main": "build/timer.cjs", | ||
"main": "build/d3-timer.js", | ||
"jsnext:main": "index", | ||
@@ -28,9 +28,9 @@ "repository": { | ||
"scripts": { | ||
"pretest": "mkdir -p build && d3-bundler -x -f cjs -o build/timer.cjs.js", | ||
"pretest": "mkdir -p build && node -e 'process.stdout.write(\"var version = \\\"\" + require(\"./package.json\").version + \"\\\"; export * from \\\"../index\\\"; export {version};\");' > build/bundle.js && rollup -f umd -u d3-timer -n d3_timer -o build/d3-timer.js -- build/bundle.js", | ||
"test": "faucet `find test -name '*-test.js'`", | ||
"prepublish": "npm run test && d3-bundler -n timer -o build/timer.js && uglifyjs build/timer.js -c -m -o build/timer.min.js && rm -f build/timer.zip && zip -j build/timer.zip -- LICENSE README.md build/timer.js build/timer.min.js" | ||
"prepublish": "npm run test && uglifyjs build/d3-timer.js -c -m -o build/d3-timer.min.js && rm -f build/d3-timer.zip && zip -j build/d3-timer.zip -- LICENSE README.md build/d3-timer.js build/d3-timer.min.js" | ||
}, | ||
"devDependencies": { | ||
"d3-bundler": "~0.4.0", | ||
"faucet": "0.0", | ||
"rollup": "0.20.5", | ||
"tape": "4", | ||
@@ -37,0 +37,0 @@ "uglify-js": "2" |
@@ -13,10 +13,10 @@ # d3-timer | ||
Schedules a new timer, invoking the specified *callback* repeatedly until it returns true. (There is no API for canceling a timer; the *callback* must return a truthy value to terminate.) An optional numeric *delay* in milliseconds may be specified to invoke the given *callback* after a delay. The delay is relative to the specified *time* in milliseconds since UNIX epoch; if *time* is not specified, it defaults to [Date.now](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now). | ||
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 since UNIX epoch; if *time* is not specified, it defaults to [Date.now](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/now). | ||
The *callback* is passed two arguments each time it is invoked: the elapsed time since the timer became active, and the current time. The latter is useful for precise scheduling of secondary timers. For example: | ||
The *callback* is passed two arguments when it is invoked: the elapsed time since the timer became active, and the current time. The latter is useful for precise scheduling of secondary timers. For example: | ||
```js | ||
timer(function(elapsed, time) { | ||
var t = timer(function(elapsed, time) { | ||
console.log(elapsed, time); | ||
return elapsed > 200; | ||
if (elapsed > 200) t.stop(); | ||
}, 150); | ||
@@ -41,3 +41,3 @@ ``` | ||
Note that the first *elapsed* is 6ms, since this is the elapsed time since the timer started, not the elapsed time since the timer was scheduled. | ||
Note that the first *elapsed* is 6ms, since this is the elapsed time since the timer started (after the 150ms delay), not the elapsed time since the timer was scheduled. | ||
@@ -50,22 +50,30 @@ Use *delay* and *time* to specify relative and absolute moments in time when the *callback* should start being invoked. For example, a calendar notification might be coded as: | ||
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. | ||
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). | ||
<a name="timer_restart" href="#timer_restart">#</a> <i>timer</i>.<b>restart</b>(<i>callback</i>[, <i>delay</i>[, <i>time</i>]]) | ||
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 [id](#timer_id) and invocation priority. | ||
<a name="timer_stop" href="#timer_stop">#</a> <i>timer</i>.<b>stop</b>() | ||
Stops this timer, preventing subsequent callbacks. This method has no effect if the timer has already stopped. | ||
<a name="timer_id" href="#timer_id">#</a> <i>timer</i>.<b>id</b> | ||
An opaque, unique identifier for this timer. | ||
<a name="timerFlush" href="#timerFlush">#</a> <b>timerFlush</b>([<i>time</i>]) | ||
Immediately execute (invoke once) any eligible timer callbacks. If *time* is specified, it represents the current time; if not specified, it defaults to Date.now. Specifying the time explicitly can ensure deterministic behavior. | ||
Immediately execute (invoke once) any eligible timer callbacks. If *time* is specified, it represents the current time; if not specified, it defaults to Date.now. Specifying an explicit time helps ensure deterministic behavior. | ||
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. | ||
<a name="timerReplace" href="#timerReplace">#</a> <b>timerReplace</b>(<i>callback</i>[, <i>delay</i>[, <i>time</i>]]) | ||
## Changes from D3 3.x: | ||
Replace the current timer’s *callback*, *delay* and *time*. This method can only be called within a timer callback, and is equivalent to [timer](#timer), except that it replaces the current timer rather than scheduling a new timer. | ||
* Returning a truthy value from the timer callback no longer has any effect; use [*timer*.stop](#timer_stop) instead. You can now stop a timer outside its callback. | ||
## Changes from D3 3.x: | ||
* The timerReplace method has been replaced by [*timer*.restart](#timer_restart). You can now restart (replace) a timer outside its callback. | ||
* Timer callbacks are now passed the current time as a second argument, in addition to the elapsed time; this is useful for precise scheduling of secondary timers. | ||
* A new [timerReplace](#timerReplace) method has been added to replace the current timer within a timer callback. | ||
* The timer.flush method has been renamed [timerFlush](#timerFlush). This method now accepts an optional *time* argument representing the current time, and returns the time of the earliest next timer. Calling this method within a timer callback no longer causes a crash. | ||
* Calling [timer](#timer) within a timer callback no longer makes a duplicate requestAnimationFrame. Calling this method with a delay greater than 24ms when no earlier timers are active guarantees a setTimeout rather than a requestAnimationFrame. |
127
src/timer.js
@@ -1,7 +0,7 @@ | ||
var queueHead, | ||
queueTail, | ||
active, // the currently-executing timer | ||
frame, // is an animation frame pending? | ||
timeout, // is a timeout pending? | ||
timeoutTime = Infinity; // the time the timeout will fire | ||
var frame = 0, // is an animation frame pending? | ||
timeout = 0, // is a timeout pending? | ||
taskHead, | ||
taskTail, | ||
taskId = 0, | ||
taskById = {}; | ||
@@ -16,75 +16,74 @@ var setFrame = typeof window !== "undefined" | ||
// The timer will continue to fire until callback returns true. | ||
export function timer(callback, delay, time) { | ||
time = time == null ? Date.now() : +time; | ||
if (delay != null) time += +delay; | ||
function Timer(callback, delay, time) { | ||
this.id = ++taskId; | ||
this.restart(callback, delay, time); | ||
} | ||
// Add the callback to the tail of the queue. | ||
var timer = {callback: callback, time: time, flush: false, next: null}; | ||
if (queueTail) queueTail.next = timer; | ||
else queueHead = timer; | ||
queueTail = timer; | ||
// Set an alarm to wake up for the timer’s first tick, if necessary. | ||
if (!frame && !active) wakeAt(time); | ||
Timer.prototype = timer.prototype = { | ||
restart: function(callback, delay, time) { | ||
if (typeof callback !== "function") throw new TypeError("callback is not a function"); | ||
time = (time == null ? Date.now() : +time) + (delay == null ? 0 : +delay); | ||
var i = this.id, t = taskById[i]; | ||
if (t) { | ||
t.callback = callback, t.time = time; | ||
} else { | ||
t = {next: null, callback: callback, time: time}; | ||
if (taskTail) taskTail.next = t; else taskHead = t; | ||
taskById[i] = taskTail = t; | ||
} | ||
sleep(); | ||
}, | ||
stop: function() { | ||
var i = this.id, t = taskById[i]; | ||
if (t) { | ||
t.callback = null, t.time = Infinity; | ||
delete taskById[i]; | ||
sleep(); | ||
} | ||
} | ||
}; | ||
// Replace the current timer. Only allowed within a timer callback. | ||
export function timerReplace(callback, delay, time) { | ||
time = time == null ? Date.now() : +time; | ||
if (delay != null) time += +delay; | ||
active.callback = callback; | ||
active.time = time; | ||
export function timer(callback, delay, time) { | ||
return new Timer(callback, delay, time); | ||
}; | ||
// Execute all eligible timers, | ||
// then flush completed timers to avoid concurrent queue modification. | ||
// Returns the time of the earliest active timer. | ||
export function timerFlush(time) { | ||
time = time == null ? Date.now() : +time; | ||
var active0 = active; | ||
// Note: timerFlush can be re-entrant, so we must preserve the old active. | ||
active = queueHead; | ||
while (active) { | ||
if (time >= active.time) active.flush = active.callback(time - active.time, time); | ||
active = active.next; | ||
} | ||
active = active0; | ||
time = Infinity; | ||
// Note: invoking a timer callback can change the timer queue (due to a re- | ||
// entrant timerFlush or scheduling a new timer). Thus we must defer capturing | ||
// queueHead until after we’ve invoked all callbacks. | ||
var t0, | ||
t1 = queueHead; | ||
while (t1) { | ||
if (t1.flush) { | ||
t1 = t0 ? t0.next = t1.next : queueHead = t1.next; | ||
} else { | ||
if (t1.time < time) time = t1.time; | ||
t1 = (t0 = t1).next; | ||
++frame; // Pretend we’ve set an alarm, if we haven’t already. | ||
try { | ||
var t = taskHead, c; | ||
while (t) { | ||
if (time >= t.time) c = t.callback, c(time - t.time, time); | ||
t = t.next; | ||
} | ||
} finally { | ||
--frame; | ||
} | ||
queueTail = t0; | ||
return time; | ||
}; | ||
function wake() { | ||
frame = timeout = 0, timeoutTime = Infinity; | ||
wakeAt(timerFlush()); | ||
frame = timeout = 0; | ||
try { | ||
timerFlush(); | ||
} finally { | ||
var t0, t1 = taskHead, time = Infinity; | ||
while (t1) { | ||
if (t1.callback) { | ||
if (time > t1.time) time = t1.time; | ||
t1 = (t0 = t1).next; | ||
} else { | ||
t1 = t0 ? t0.next = t1.next : taskHead = t1.next; | ||
} | ||
} | ||
taskTail = t0; | ||
sleep(time); | ||
} | ||
} | ||
function wakeAt(time) { | ||
function sleep(time) { | ||
if (frame) return; // Soonest alarm already set, or will be. | ||
if (timeout) timeout = clearTimeout(timeout); | ||
var delay = time - Date.now(); | ||
if (delay > 24) { | ||
if (timeoutTime > time) { // Note: false if time is infinite. | ||
if (timeout) clearTimeout(timeout); | ||
timeout = setTimeout(wake, delay); | ||
timeoutTime = time; | ||
} | ||
} else { | ||
if (timeout) timeout = clearTimeout(timeout), timeoutTime = Infinity; | ||
frame = setFrame(wake); | ||
} | ||
if (delay > 24) { if (time < Infinity) timeout = setTimeout(wake, delay); } | ||
else frame = 1, setFrame(wake); | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
77
14194
175
1