Detecting forceSimulation termination without a stopped event
0 reputation · 10 Sept 2024, 09:14 UTC
Goal
Determine when a d3-force simulation has truly stopped so that downstream layout cleanup, snapshot capture, or component unmounting can run without race conditions.
Constraints and uncertainty
The simulation cools by decaying an internal alpha value toward alphaTarget (default 0) at a rate set by alphaDecay. When alpha falls below alphaMin (default 0.001), tick events cease — but no stopped event or promise is emitted. Interactive drag temporarily raises alphaTarget to 0.3, extending runtime to hundreds or thousands of ticks. Calling simulation.stop() forces alpha to 0 immediately, yet restart() resumes from that zero value unless alpha(1) is reset first. In React, Vue, or Svelte, failing to call stop() in cleanup leaks tick listeners. TypeScript definitions mark alphaMin, alphaDecay, and alphaTarget as read-only after construction, requiring setter methods for mutation.
Questions
- What minimal polling or wrapper pattern reliably signals termination across all
alphaTargetchanges without adding per-tick overhead? - How should a consumer coordinate asynchronous cleanup when the simulation may be stopped programmatically or allowed to cool naturally?
- Is a custom event emitter wrapped around
tickthe only robust way to expose astoppedsignal without forking d3-force?