This module provides an efficient queue capable of managing thousands of concurrent animations, while guaranteeing consistent, synchronized timing with concurrent or staged animations.
I ran into an issue with d3.interval not working as expected when calling restart. If I create a new interval, it works as expected -- firing the callback every delay milliseconds:
If I call restart on this instance, passing the same callback and delay, I would expect intervalTest to restart following the same behavior. However, it appears to start a timer (or an interval with 0ms delay) instead.
Like timer, except the callback is invoked only every delay milliseconds; if delay is not specified, this is equivalent to timer. A suitable replacement for setInterval that is guaranteed to not run in the background. The callback is passed the elapsed time.
It might be also worth adding that unlike setInterval it is also not trying to catch up, which depending on the use good case might be a good thing or not (in my experience it's always better this way and relying on the catch-up behavior is IMHO likely a sign of a suboptimal approach).
I went to find out about interval. when I change the ms values to a lower value they just go fast and then you can't reset it. you have to refresh the browser. It could just be a bug on his webpage.
However, on my system I am trying to use it and look at the logs of what the elapsed time is returning time. It is like it is going backward in time. How is this possible. Is this a bug or just my system. I am developing on my localhost but still this seems very erratic
app.component.ts:626 TIGER 7 MADEN 94952.90000003576
00:18:50.260 app.component.ts:627 TIGER 8 MADEN 94944
00:18:50.261 app.component.ts:626 TIGER 7 MADEN 92480.40000003576
00:18:50.261 app.component.ts:627 TIGER 8 MADEN 92471.5
00:18:50.261 app.component.ts:626 TIGER 7 MADEN 7247.5
00:18:50.261 app.component.ts:627 TIGER 8 MADEN 7238.599999964237
00:18:50.270 app.component.ts:626 TIGER 7 MADEN 94962.40000003576
00:18:50.270 app.component.ts:627 TIGER 8 MADEN 94952.90000003576
00:18:50.270 app.component.ts:626 TIGER 7 MADEN 92489.90000003576
00:18:50.271 app.component.ts:627 TIGER 8 MADEN 92480.40000003576
00:18:50.271 app.component.ts:626 TIGER 7 MADEN 7257
00:18:50.271 app.component.ts:627 TIGER 8 MADEN 7247.5
00:18:50.281 app.component.ts:626 TIGER 7 MADEN 94973.30000001192
00:18:50.281 app.component.ts:627 TIGER 8 MADEN 94962.40000003576
00:18:50.281 app.component.ts:626 TIGER 7 MADEN 92500.80000001192
00:18:50.281 app.component.ts:627 TIGER 8 MADEN 92489.90000003576
00:18:50.282 app.component.ts:626 TIGER 7 MADEN 7267.899999976158
00:18:50.282 app.component.ts:627 TIGER 8 MADEN 7257
00:18:50.289 app.component.ts:626 TIGER 7 MADEN 94981.5
00:18:50.289 app.component.ts:627 TIGER 8 MADEN 94973.30000001192
app.component.ts:627 TIGER 8 MADEN 192203.5
00:20:27.520 app.component.ts:626 TIGER 7 MADEN 189739.10000002384
00:20:27.520 app.component.ts:627 TIGER 8 MADEN 189731
00:20:27.520 app.component.ts:626 TIGER 7 MADEN 104506.19999998808
00:20:27.520 app.component.ts:627 TIGER 8 MADEN 104498.09999996424
00:20:27.528 app.component.ts:626 TIGER 7 MADEN 192220
00:20:27.528 app.component.ts:627 TIGER 8 MADEN 192211.60000002384
00:20:27.528 app.component.ts:626 TIGER 7 MADEN 189747.5
00:20:27.528 app.component.ts:627 TIGER 8 MADEN 189739.10000002384
00:20:27.528 app.component.ts:626 TIGER 7 MADEN 104514.59999996424
00:20:27.528 app.component.ts:627 TIGER 8 MADEN 104506.19999998808
00:20:27.536 app.component.ts:626 TIGER 7 MADEN 192228.40000003576
00:20:27.536 app.component.ts:627 TIGER 8 MADEN 192220
00:20:27.536 app.component.ts:626 TIGER 7 MADEN 189755.90000003576
00:20:27.536 app.component.ts:627 TIGER 8 MADEN 189747.5
00:20:27.537 app.component.ts:626 TIGER 7 MADEN 104523
00:20:27.537 app.component.ts:627 TIGER 8 MADEN 104514.59999996424
00:20:27.544 app.component.ts:626 TIGER 7 MADEN 192236.69999998808
00:20:27.545 app.component.ts:627 TIGER 8 MADEN 192228.40000003576
00:20:27.545 app.component.ts:626 TIGER 7 MADEN 189764.19999998808
00:20:27.545 app.component.ts:627 TIGER 8 MADEN 189755.90000003576
00:20:27.545 app.component.ts:626 TIGER 7 MADEN 104531.29999995232
00:20:27.545 app.component.ts:627 TIGER 8 MADEN 104523
00:20:27.553 app.component.ts:626 TIGER 7 MADEN 192245.19999998808
00:20:27.553 app.component.ts:627 TIGER 8 MADEN 192236.69999998808
00:20:27.553 app.component.ts:626 TIGER 7 MADEN 189772.69999998808
00:20:27.553 app.component.ts:627 TIGER 8 MADEN 189764.19999998808
00:20:27.554 app.component.ts:626 TIGER 7 MADEN 104539.79999995232
00:20:27.554 app.component.ts:627 TIGER 8 MADEN 104531.29999995232
00:20:27.561 app.component.ts:626 TIGER 7 MADEN 192253.40000003576
00:20:27.561 app.component.ts:627 TIGER 8 MADEN 192245.199999988
i seem to have memory leak from using the transition which uses the Timer object.
it's seems to me like it doesn't clean it though i dont keep references to any transition.
I built a live updating graph with d3.js v4.4 and angularjs 1.4.8 displaying nodes and connections which uses transition on updates.
i found out i have memory leaks on my application whether it stays open when i'm on the tab & not on the tab for a long time, i also check with task manager.
i added a screenshot showing my memory heap snap shoot, the Timer object seems to not be cleaning up and i'm really stuck about this. Heap snapshot
an update flow example:
$interval(() => {
getSnapshot()
}, 30)
function getSnapshot(timestamp){
let snapshotTs = timestamp ? timestamp : moment()
getSnapshotFromServer(snapshotTs).then(data=>{
graph = data.graph //contains all nodes and connections
buildGraph(graph)
})
}
buildGraph(){
//build graph with connections and nodes using transition
//we use tr
}
I was looking into how I can gracefully resume a timer that might have been stopped and thought this would have been a natural choice, but noticed the function didn't return anything. This is helpful for pausing and resuming mid-way through an animation. If I restarted the timer, it will likely interrupt the timer and restart the animation.
The app I'm working is used for monitoring. A common use case is to have a tab open for days.
I noticed that when a tab containing a d3 component becomes active again after a long time being inactive it causes the page to freeze.
I hijack the native requestAnimationFrame and simulate the tab being inactive to speed up the manifestation of this issue (Since the browser also throttles setInterval calls), but this can be reproduces without these measures by setting up a setInterval loop that schedules transitions, switching to another tab and then waiting for ~1 hour or so.
I'm aware the test case is extreme, but this happens in our app with much slower updates (and longer inactive periods)
Any ideas how this can be mitigated?
Thanks,
Guy
*not sure if this should go to d3-transition or here, but it seems related to #17
I use victory library (for generating chart based on d3.js) and on unmounting process it uses timer.now(), which causes (sometimes) "Invalid calling object". From what I read it's connected with passing references to native functions.
I imagine that this is the problem: setFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : function(f) { setTimeout(f, 17); };
In my case calling setFrame(clearNow) results in "Invalid calling object",
but calling requestAnimationFrame(clearNow)works just fine.
Open a html page with d3 transition in browser.
Navigate away from page before transition happen.
Say away until file is loaded.
Come back and you will find the transition will take 10-20 sec to start giving impression that chart is broken.
I found the bug in timeflush() function which is not calculating the right clocknow variable.
functiontimerFlush(){now();// Get the current time, if not already set.++frame;// Pretend we’ve set an alarm, if we haven’t already.vart=taskHead,e;while(t){if((e=clockNow-t._time)>=0)t._call.call(null,e);t=t._next;}--frame;}
clocknow should be positive when tab wake up but its not causing next transition call to stop.
I'm utilizing d3js in an electron.js application and when the system is left unattended for a day or so, when it wakes up the application isn't responsive. Initially I thought it was related to the system going to sleep, but in my most recent test the system never went to sleep, it was just locked for a couple of days.
Eventually it becomes responsive again, the application is fine, and the chrome console reports
[Violation] 'requestAnimationFrame' handler took Xms in timer.js:67
In my most recent test it was 24046ms (24s) after leaving it locked for about 60 hours.
The location that it reports is in the wake() function on line 67 of timer.js.
Based looking at what wake() does, the implication is that there's a very large number of time tasks being iterated over when the application is being woken up or it's doing some other operation to "catch up". I'm going to try to add some more instrumentation to see more precisely where the time is going, curious if this is a known issue and if there's a workaround.
I have an ES6 Class function that I'd like to invoke as a callback function, but it loses the scope of this when I call it with timer(callback).
Any way we can pass a custom variable to the callback function? I'm not quite sure how the syntax would even look, as I wouldn't want to override the delay and time variables.
In some browsers, requestAnimationFrame is paused in background tabs. However, setTimeout and setInterval may continue, albeit possibly throttled. This asymmetrical behavior can have undesired side-effects, such as an ever-growing timer queue that is never flushed because timers are being scheduled but never invoked.
One way to fix this might be to guarantee that the first tick of a scheduled, eligible timer is invoked after at most one second, even in a background tab, by using setTimeout or setInterval internally rather than relying solely on requestAnimationFrame. We could guarantee that timers receive at least one tick per second this way, but I think it would be sufficient to only have this special behavior for the first tick—it’s nice if we do as little as possible in the background.
Or, we could just tick at least once per second, no matter what.
Hello,
a simplenpm install; npm run test fails now:
# timer(callback, delay) first invokes the callback after the specified delay
not ok 47 should be in range
---
operator: inRange
expected: [ 140, 160 ]
actual: 166
at: Test.tape.Test.inRange (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/test/inRange.js:4:8)
stack: |-
Error: should be in range
at Test.assert [as _assert] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/node_modules/tape/lib/test.js:224:54)
at Test.bound [as _assert] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/node_modules/tape/lib/test.js:76:32)
at Test.tape.Test.inRange (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/test/inRange.js:4:8)
at Test.bound [as inRange] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/node_modules/tape/lib/test.js:76:32)
at /home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/test/timer-test.js:69:10
at timerFlush (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/build/d3-timer.js:68:48)
at Timeout.wake [as _onTimeout] (/home/xavier/dev/debian/src/pkg-js/packages/node-d3-timer/build/d3-timer.js:78:5)
at ontimeout (timers.js:436:11)
at tryOnTimeout (timers.js:300:5)
at listOnTimeout (timers.js:263:5)
...
# timer(callback, delay) computes the elapsed time relative to the delay
ok 48 should be in range
The second timer is invoked in the same tick as the first timer, since it is added to the queue while it is being flushed. Yet, requestAnimationFrame is still called because frame == 0 during flush.
I have build a little test App and I hoped that d3.timer() would make it much faster by using requestAnimationFrame(), but sadly the opposite is true. It is even slower than using setInterval( ... ,0.00001).
Ok the version with d3.timer() is more responsive, but thats not too impressive since its overall speed is much slower. In the attached video I tried to generate a random walk from prime numbers. But with both methods it simply is too slow...
Maybe I've done something wrong in my algorithm, so that requestAnimationFrame() can not be used, but I would not know what that could be.
A video comparing d3.timer() and setInterval( ... ,0.00001) (setInterval() is on left side): setInterval_vs_d3.timer.zip
Uncaught DOMException: Failed to read the 'value' property from 'SVGLength': Could not resolve relative length.
at SVGSVGElement.defaultExtent (http://localhost:3000/webpack-internal:///2894:29:25)
at new Gesture (http://localhost:3000/webpack-internal:///2894:198:26)
at gesture (http://localhost:3000/webpack-internal:///2894:190:12)
at SVGSVGElement.eval (http://localhost:3000/webpack-internal:///2894:164:40)
at Dispatch.call (http://localhost:3000/webpack-internal:///2631:57:72)
at start (http://localhost:3000/webpack-internal:///121:130:13)
at schedule (http://localhost:3000/webpack-internal:///121:78:32)
at timerFlush (http://localhost:3000/webpack-internal:///703:65:48)
at wake (http://localhost:3000/webpack-internal:///703:75:5)
defaultExtent @ zoom.js?6f4f:20
Gesture @ zoom.js?6f4f:189
gesture @ zoom.js?6f4f:181
(anonymous) @ zoom.js?6f4f:155
call @ dispatch.js?8f20:57
start @ schedule.js?f489:118
schedule @ schedule.js?f489:66
timerFlush @ timer.js?a6a1:61
wake @ timer.js?a6a1:71
requestAnimationFrame (async)
sleep @ timer.js?a6a1:108
restart @ timer.js?a6a1:39
timer @ timer.js?a6a1:52
create @ schedule.js?f489:59
__webpack_exports__.e @ schedule.js?f489:19
__webpack_exports__.a @ transition.js?7712:36
...
I think d3 needs to call cancelAnimationFrame if possible when it is being removed. Otherwise, wake function below could be called while no element is mounted on DOM for doing something.
d3.timerOnce is a good replacement for setTimeout, so let’s call it d3.timeout?
And, what about a d3.interval that’s a replacement for setInterval? Then we can have a setInterval that doesn’t run in the background; related #6d3/d3-transition#29.
I'm using RStudio, an R language IDE, for developing an htmlwidget with d3. I'm trying to port my code to d3 v4, and I'm facing a curious problem: transitions don't work anymore. They work fine with d3 v3 in RStudio or with d3 v4 in a browser, but not with d3 v4 in RStudio.
After a bit of digging, it seems that the problem comes from the toolkit used by RStudio (GWT 2.7.0 as far as I can tell, but I don't know what kind and version of browser it is. The user agent displayed in the "about" dialog is currently "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) RStudio Safari/538.1 Qt/5.4.0").
There seems to be an inconsistency in the results returned by performance.now and requestAnimationFrame :
If I understand correctly, It seems that performance.now() returns an high precision timestamp, whereas requestAnimationFrame gives a number of milliseconds since epoch.
Due to this inconsistency, timers and transitions created by d3 don't work at all.
I've found a quick and dirty workaround inspired by this link. Adding the following code in d3-timer.js just before the now() function definition seems to work :
I could generate a pull request for this if you wish, but I'm really not sure the code quality is good enough, neither that this kind of workaround would really have a place in d3 code. Or there may be a better solution.
Sorry for this quite long issue, and thanks a lot for this awesome library and the no less awesome documentation and examples !
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible" content="IE=edge"><metaname="viewport" content="width=device-width, initial-scale=1.0"><title>d3-timer memory leak</title><scriptsrc="https://cdn.jsdelivr.net/npm/d3-timer@3"></script><script>constt=d3.timer((elapsed)=>{// run your code},150);</script><body></body></html>
See the console output. The second callback is called with the same elapsed time as the first one, even though the first one has a considerable execution time. On my computer the elapsed time error is as much as 376 ms.
Related d3/d3#802. Since we use requestAnimationFrame internally, all animations run at 60 FPS. It might be nice to allow animations at other frame rates… but it’s not clear how that API should be exposed.