From fb43e7d4135d5ce42fe1643f7250023e5f2c272e Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Tue, 9 Feb 2016 15:51:19 -0800 Subject: [PATCH] Optimizations. Private state on the timer object; avoid try-finally. --- README.md | 8 ++---- package.json | 4 +-- src/timer.js | 66 +++++++++++++++++++++++----------------------- test/timer-test.js | 8 ------ 4 files changed, 37 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index a850d69..4580c34 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This module provides an efficient queue capable of managing thousands of concurr If you use NPM, `npm install d3-timer`. Otherwise, download the [latest release](https://github.com/d3/d3-timer/releases/latest). The released bundle supports AMD, CommonJS, and vanilla environments. Create a custom build using [Rollup](https://github.com/rollup/rollup) or your preferred bundler. You can also load directly from [d3js.org](https://d3js.org): ```html - + ``` In a vanilla environment, a `d3_timer` global is exported. [Try d3-timer in your browser.](https://tonicdev.com/npm/d3-timer) @@ -55,16 +55,12 @@ If [timer](#timer) is called within the callback of another timer, the new timer # timer.restart(callback[, delay[, time]]) -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. +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() Stops this timer, preventing subsequent callbacks. This method has no effect if the timer has already stopped. -# timer.id - -An opaque, unique identifier for this timer. - # d3.timerOnce(callback[, delay[, time]]) Like [timer](#timer), except the timer automatically [stops](#timer_stop) on its first callback. diff --git a/package.json b/package.json index d4f4a62..0e4fc94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "d3-timer", - "version": "0.1.2", + "version": "0.2.0", "description": "An efficient queue capable of managing thousands of concurrent animations.", "keywords": [ "d3", @@ -27,7 +27,7 @@ "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 -n d3_timer -o build/d3-timer.js -- build/bundle.js", "test": "faucet `find test -name '*-test.js'` && eslint index.js src", "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", - "postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git tag -am \"Release $VERSION.\" v${VERSION} && git push --tags && cp build/d3-timer.js ../d3.github.com/d3-timer.v0.1.js && cp build/d3-timer.min.js ../d3.github.com/d3-timer.v0.1.min.js && cd ../d3.github.com && git add d3-timer.v0.1.js d3-timer.v0.1.min.js && git commit -m \"d3-timer ${VERSION}\" && git push" + "postpublish": "VERSION=`node -e 'console.log(require(\"./package.json\").version)'`; git push && git tag -am \"Release $VERSION.\" v${VERSION} && git push --tags && cp build/d3-timer.js ../d3.github.com/d3-timer.v0.2.js && cp build/d3-timer.min.js ../d3.github.com/d3-timer.v0.2.min.js && cd ../d3.github.com && git add d3-timer.v0.2.js d3-timer.v0.2.min.js && git commit -m \"d3-timer ${VERSION}\" && git push" }, "devDependencies": { "faucet": "0.0", diff --git a/src/timer.js b/src/timer.js index d107efc..362be57 100644 --- a/src/timer.js +++ b/src/timer.js @@ -1,9 +1,7 @@ var frame = 0, // is an animation frame pending? timeout = 0, // is a timeout pending? taskHead, - taskTail, - taskId = 0, - taskById = {}; + taskTail; var setFrame = typeof window !== "undefined" && (window.requestAnimationFrame @@ -14,28 +12,28 @@ var setFrame = typeof window !== "undefined" || function(callback) { return setTimeout(callback, 17); }; function Timer() { - this.id = ++taskId; + this._call = + this._time = + this._next = null; } 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; + if (!this._call) { + if (taskTail) taskTail._next = this; + else taskHead = this; + taskTail = this; } + this._call = callback; + this._time = time; sleep(); }, stop: function() { - var i = this.id, t = taskById[i]; - if (t) { - t.callback = null, t.time = Infinity; - delete taskById[i]; + if (this._call) { + this._call = null; + this._time = Infinity; sleep(); } } @@ -56,15 +54,26 @@ export function timerOnce(callback, delay, time) { export function timerFlush(now) { now = now == null ? Date.now() : +now; ++frame; // Pretend we’ve set an alarm, if we haven’t already. - try { - var t = taskHead, c; - while (t) { - if (now >= t.time) c = t.callback, c(now - t.time, now); - t = t.next; + var t = taskHead; + while (t) { + if (now >= t._time) t._call.call(null, now - t._time, now); + t = t._next; + } + --frame; +} + +function timerSweep() { + var t0, t1 = taskHead, time = Infinity; + while (t1) { + if (t1._call) { + if (time > t1._time) time = t1._time; + t1 = (t0 = t1)._next; + } else { + t1 = t0 ? t0._next = t1._next : taskHead = t1._next; } - } finally { - --frame; } + taskTail = t0; + return time; } function wake() { @@ -72,17 +81,8 @@ function wake() { 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); + frame = 0; + sleep(timerSweep()); } } diff --git a/test/timer-test.js b/test/timer-test.js index d5d6c9c..8876bd5 100644 --- a/test/timer-test.js +++ b/test/timer-test.js @@ -387,11 +387,3 @@ tape("timer.restart(callback, delay, time) recomputes the new wake time after on }, 100); }, 100); }); - -tape("timer.id is a positive integer", function(test) { - var t = timer.timer(function() {}); - test.ok(t.id > 0); - test.equal(t.id % 1, 0); - t.stop(); - end(test); -});