npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@marianmeres/ticker

v1.16.5

Published

[![NPM version](https://img.shields.io/npm/v/@marianmeres/ticker)](https://www.npmjs.com/package/@marianmeres/ticker) [![JSR version](https://jsr.io/badges/@marianmeres/ticker)](https://jsr.io/@marianmeres/ticker)

Readme

@marianmeres/ticker

NPM version JSR version

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/ticker
deno add jsr:@marianmeres/ticker

Basic 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.