@marianmeres/ticker
v1.16.5
Published
[](https://www.npmjs.com/package/@marianmeres/ticker) [](https://jsr.io/@marianmeres/ticker)
Readme
@marianmeres/ticker
Do something when it ticks. With a store-compatible API.
Under the hood it uses recursive setTimeout with interval corrections, so it should
guarantee precise frequency except for edge cases where the synchronous
subscriber's work cannot keep up with the interval — it will never tick again before the
previous tick finishes.
Install
npm i @marianmeres/tickerdeno add jsr:@marianmeres/tickerBasic Usage
import { createTicker } from "@marianmeres/ticker";
// Create a ticker that ticks every 1000 milliseconds
const ticker = createTicker(1000);
// Subscribe to tick events
const unsub = ticker.subscribe((timestamp) => {
if (timestamp) {
// Do something on each tick
console.log("Tick at", timestamp);
} else {
// Ticker is stopped (or has not started yet)
console.log("Ticker stopped");
}
});
// Start the ticker
ticker.start();
// Later: stop and clean up
ticker.stop();
unsub();RAF Ticker
For animations or visual updates that should sync with the browser's repaint cycle, use
createTickerRAF:
import { createTickerRAF } from "@marianmeres/ticker";
const ticker = createTickerRAF(1000 / 60); // ~60fps
ticker.subscribe((timestamp) => {
if (timestamp) updateAnimation();
});
ticker.start();The RAF ticker uses requestAnimationFrame internally, with a polyfill for non-browser
environments. The minimum effective interval is ~16.67ms (60Hz).
Delayed Worker Ticker
Using a tick signal from a sync ticker for asynchronous work (e.g., periodic API
fetching)
may not be the best choice.
For such cases, use createDelayedWorkerTicker. Instead of guaranteeing frequency, it
guarantees a delay between worker calls:
import { createDelayedWorkerTicker } from "@marianmeres/ticker";
// Execute async work, then pause for 5 seconds, then repeat...
const ticker = createDelayedWorkerTicker(
async () => {
const response = await fetch("/api/data");
return response.json();
},
5000,
);
ticker.subscribe(({ started, finished, error, result }) => {
if (started && !finished) {
// Worker is in progress
console.log("Fetching...");
} else if (started && finished && !error) {
// Worker completed successfully
console.log("Got data:", result);
} else if (error) {
// Worker threw an error
console.error("Fetch failed:", error);
} else {
// Ticker is stopped (or has not started yet)
}
});
ticker.start();Dynamic Intervals
The interval can be a function that returns the number of milliseconds. This allows for dynamic timing:
// Exponential backoff example
let attempts = 0;
const ticker = createTicker((previousInterval) => {
return Math.min(1000 * Math.pow(2, attempts++), 30000);
});The function receives the previous interval value and the current store value as arguments:
type Interval = number | ((previous: number, storeVal: any) => number);Chaining API
All control methods return the ticker instance for chaining:
const unsub = createTicker(1000)
.setInterval(500)
.start()
.subscribe((v) => console.log(v));
// Later
ticker.stop();
unsub();Error Handling
Subscriber errors are caught and logged to prevent breaking the ticker. The ticker will continue running even if a subscriber throws:
ticker.subscribe((v) => {
if (v && someCondition) {
throw new Error("Oops!"); // Won't break the ticker
}
});You can provide a custom error handler using the options object signature:
const ticker = createTicker(1000, {
onError: (error) => {
// Custom error handling (or silence errors)
myLogger.warn("Subscriber error:", error);
},
});API Reference
See API.md for full API documentation.
Factory Functions
| Function | Description |
| --------------------------- | -------------------------------------------------- |
| createTicker | Fixed-frequency timer with drift correction |
| createTickerRAF | RAF-synchronized timer for animations |
| createDelayedWorkerTicker | Timer for async work with delay between executions |
Ticker Methods
All ticker instances provide: subscribe(), start(), stop(), toggle(),
isStarted(), setInterval(), getInterval().
Helper Exports
isBrowser(), getRaf(), setTimeoutRAF() — primarily for internal use.
