d3-timer: efficient concurrent animation queue
d3-timer provides: d3.now() gets the current high-resolution time. d3.timer(callback, delay, time) schedules a new timer. timer.restart(callback, delay, time) resets the timer's start time and callback. timer.stop() stops the timer. d3.timerFlush() immediately executes any eligible timers. d3.timeout(callback, delay, time) schedules a timer that stops on its first callback. d3.interval(callback, delay, time) schedules a timer called with a configurable period.
d3-timer module purpose
The d3-timer 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 for fluid animation if available, switching to setTimeout for delays longer than 24ms.
d3.now() signature and behavior
d3.now() returns the current time as defined by performance.now if available, and Date.now if not. The current time is updated at the start of a frame and is thus consistent during the frame. Any timers scheduled during the same frame will be synchronized. If 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.
d3.timer() signature and parameters
d3.timer(callback, delay, time) schedules a new timer, invoking the specified callback repeatedly until the timer is stopped. The callback is passed the elapsed time since the timer became active. An optional numeric delay in milliseconds may be specified to invoke the 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().
d3.timer() callback ordering within frames
If 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.
d3.timer() elapsed time and apparent time
The elapsed time passed to the timer callback is the elapsed time since the timer became active (after the delay), not since it was scheduled. The apparent elapsed time may be less than the true elapsed time if the page is backgrounded and requestAnimationFrame is paused; in the background, apparent time is frozen.
timer.restart() signature and behavior
timer.restart(callback, delay, time) restarts a timer with the specified callback and optional delay and time. This is equivalent to stopping the timer and creating a new timer with the specified arguments, although the timer retains the original invocation priority.
timer.stop() signature and behavior
timer.stop() stops the timer, preventing subsequent callbacks. This method has no effect if the timer has already stopped.
d3.timerFlush() signature and purpose
d3.timerFlush() immediately invokes any eligible timer callbacks. Zero-delay timers are normally first executed after one frame (~17ms), which 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. Flushing the timer queue at the end of the first event loop runs zero-delay timers immediately and avoids the flicker.
d3.timeout() signature and behavior
d3.timeout(callback, delay, time) is like timer(), except the timer automatically stops on its first callback. It is a suitable replacement for setTimeout that is guaranteed to not run in the background. The callback is passed the elapsed time.
d3.interval() signature and behavior
d3.interval(callback, delay, time) is like timer(), except the callback is invoked only every delay milliseconds. If delay is not specified, this is equivalent to timer(). It is a suitable replacement for setInterval that is guaranteed to not run in the background. The callback is passed the elapsed time.
d3.now() example output
d3.now() returns a number representing milliseconds since an arbitrary origin. Example: d3.now() returns 1236.3000000715256.
d3.timer() usage example with elapsed callback and stop
Example showing how to use d3.timer() with a callback that logs elapsed time and stops the timer after 200ms:
const t = d3.timer((elapsed) => {
console.log(elapsed);
if (elapsed > 200) t.stop();
}, 150);
This example has a 150ms delay and produces console output showing elapsed times: 3, 25, 48, 65, 85, 106, 125, 146, 167, 189, 209 (exact values vary depending on the JavaScript runtime).