As of 3.1, the timer and limiter utilities ship as part of the @repeaterjs/repeater package, available from dedicated subpath exports. You no longer install a separate package — just import from @repeaterjs/repeater/timers or @repeaterjs/repeater/limiters.

Migrating from the standalone packages? The old @repeaterjs/timers and @repeaterjs/limiters packages are deprecated in favor of these subpaths, and the helpers gained a create prefix: delaycreateDelay, intervalcreateInterval, timeoutcreateTimeout, semaphorecreateSemaphore, throttlecreateThrottle. The @repeaterjs/pubsub and @repeaterjs/react-hooks packages are not carried forward.

Timers

import {
  createDelay,
  createInterval,
  createTimeout,
} from "@repeaterjs/repeater/timers";
import { createInterval } from "@repeaterjs/repeater/timers";

(async () => {
  let count = 0;
  for await (const _ of createInterval(1000)) {
    console.log(`tick ${count++}`);
    if (count >= 3) break; // breaking cleans up the underlying timer
  }
})();

Because these are repeaters, calling return (or breaking out of a for await loop) clears the underlying setTimeout/setInterval for you.

Limiters

import { createSemaphore, createThrottle } from "@repeaterjs/repeater/limiters";
import { createSemaphore } from "@repeaterjs/repeater/limiters";

// Run at most two `fetch`es concurrently.
const sem = createSemaphore(2);
async function fetchThrottled(url) {
  const { value: token } = await sem.next();
  try {
    return await fetch(url);
  } finally {
    token.release();
  }
}

A token carries { id, limit, remaining, release() }; throttle tokens also include a reset timestamp indicating when the rate window next opens.