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 🙏

© 2025 – Pkg Stats / Ryan Hefner

timeout-flow

v0.0.17

Published

Fluent, composable, pauseable JavaScript timers and time control flows — plus RAF utilities for frame-based logic.

Readme

TimeoutFlow

npm gzip size downloads GitHub stars

Fluent, human-readable time control for JavaScript.

TimeoutFlow makes working with time-based logic intuitive — think of it as a modern, composable upgrade to setTimeout and setInterval, with added powers like chaining, conditional logic, pause/resume, retries, and more.

  • Minified: 6.64 KB
  • Gzipped: 2.6 KB

Philosophy

TimeoutFlow is not just a wrapper for setTimeout. It's a composable mini-framework for expressing time as fluent logic.

We believe temporal behavior in JavaScript should be:

  • Readable – durations like "1s" and "500ms" are easier to reason about than magic numbers.
  • Composable – sequencing events should be declarative, not a tangle of nested callbacks or timers.
  • Controllable – any timer should be pauseable, resumable, and cancelable at any moment.
  • Branchable – real flows require if, while, label, and jumpTo() — not just repetition.
  • Tiny – no dependencies, no bloat, and no reactivity engine required.

TimeoutFlow gives you atomic time primitives (after, every, debounce, retry) and a fluent builder (flow()) to script rich behavior over time — like a timeline you can control.

In Other Words:

Think of TimeoutFlow as setTimeout() with superpowers. But more importantly, think of it as a way to write time like you write logic.

flow()
  .after('1s', () => console.log('Start'))
  .every('500ms', (i) => console.log(`Tick ${i}`), 3)
  .after('1s', () => console.log('Done'))
  .start();

This isn’t about wrapping timers. It’s about orchestrating intent — clearly, fluently, and with full control.


Installation

npm install timeout-flow

Features

  • after("1s", fn) — delay execution (via AfterTimer)

  • every("500ms", fn, count?) — repeat execution with optional limit (via EveryTimer)

  • flow() — create fluent, chainable timelines with:

    • .after(), .every(), .loop(n)
    • .if(), .unless(), .label(), .jumpTo()
    • .while(), .doWhile()
  • Utilities: debounce(), throttle(), retry(), waitFor()


Usage Examples

// 1. Delayed Execution
import { after } from 'timeout-flow';
after('2s', () => console.log('Waited 2 seconds...'));

// 2. Repeating with Pause & Resume
import { every, after as wait } from 'timeout-flow';
const ticker = every('1s', i => console.log(`Tick ${i}`), 5);
wait('2.5s', () => ticker.pause());
wait('4s', () => ticker.resume());

// 3. Debounced Input
import { debounce } from 'timeout-flow';
const search = debounce('300ms', (e) => {
  console.log('Searching for:', e.target.value);
});
document.querySelector('input').addEventListener('input', search);

// 4. Retry a Failing Request
import { retry } from 'timeout-flow';
await retry(() => fetch('/api/data'), {
  attempts: 4,
  delay: '1s',
  backoff: true
});

// 5. Wait for DOM Change
import { waitFor } from 'timeout-flow';
await waitFor(() => document.querySelector('#loaded'), {
  interval: '250ms',
  timeout: '5s'
});
console.log('Element loaded!');

// 6. Fluent Timeline
import { flow } from 'timeout-flow';
flow()
  .after('1s', () => console.log('Step 1'))
  .every('500ms', (i) => console.log(`Tick ${i}`), 3)
  .after('1s', () => console.log('Final Step'))
  .start();

// 7. Conditional & Labeled Logic
let debug = true;
flow()
  .after('1s', () => console.log('Boot sequence'))
  .if(() => debug)
  .after('500ms', () => console.log('Debug logs enabled'))
  .label('loop')
  .every('1s', i => console.log(`Frame ${i}`), 3)
  .after('500ms', () => console.log('Restarting...'))
  .jumpTo('loop')
  .start();

// 8. Controlled Loop
let energy = 3;
flow()
  .doWhile(() => energy-- > 0)
  .every('400ms', () => console.log(`Blast (${energy})`))
  .after('1s', () => console.log('Energy depleted'))
  .start();

Utilities

import { debounce, throttle, retry, waitFor } from 'timeout-flow';
  • debounce('300ms', fn) — Run only after silence
  • throttle('1s', fn) — Run at most once per time window
  • retry(fn, { attempts, delay, backoff }) — Resilient retry for async calls
  • waitFor(() => condition, { timeout, interval }) — Await condition change

Frame-Based Timing (RAF Utilities)

These helpers use requestAnimationFrame under the hood to provide smooth, energy-efficient timing. Ideal for visual UI flows, canvas apps, scroll/resize behavior, and performance-sensitive interactions.

All raf utilities automatically pause in background tabs, unlike timers.

API Summary

| Function | Purpose | Best For | | -------------------------- | ------------------------------------------------------------------- | -------------------------------------------------- | | afterRaf() | Runs a function once after N ms, using requestAnimationFrame. | Idle effects, UI post-load, paint batching | | everyRaf() | Repeats a function every N ms, throttled via frames. | Sanity loops, smooth polling, visual checks | | debounceRaf() | Debounces a function using frames instead of timeouts. | Drag/move handlers, visual updates | | debounceRaf('300ms', fn) | Debounces like traditional debounce, but frame-aware. | Resize events, paused background flows | | throttleRaf() | Throttles execution to at most once per frame. | Scroll events, pointermove, paint-heavy flows | | throttleRaf(fn, 2) | Throttles to once every 3 frames (frameSkip = 2). | Advanced visuals, slower sync without timers | | waitForRaf() | Waits for a condition to become true using a frame-based loop. | DOM readiness, layout stability, visibility checks |

Key Advantages

  • Frame-sync: Triggered in sync with visual updates (60Hz or higher)
  • Background-tab safe: No CPU use when inactive
  • Energy efficient: Great for battery-conscious apps
  • Smoother UX: Especially under load or heavy visuals

🛠 Example Usage

import { debounceRaf } from 'timeout-flow';

const onMouseMove = debounceRaf(() => {
  drawPreview();
});

const onResize = debounceRaf('250ms', () => {
  updateLayout();
});
import { afterRaf } from 'timeout-flow';

afterRaf('2s', () => {
  showIntroAnimation();
});
import { everyRaf } from 'timeout-flow';

const loop = everyRaf('1s', () => {
  console.log('heartbeat');
});
import { throttleRaf } from 'timeout-flow';

const onScroll = throttleRaf((e) => {
  handleScroll(e);
});

const onDrag = throttleRaf(drawFrame, 2);
import { waitForRaf } from 'timeout-flow';

await waitForRaf(() => document.querySelector('#panel')?.offsetHeight > 0);

File Locations

| File | Description | | -------------------- | ------------------------------------------------------ | | raf/afterRaf.js | One-time timer with frame pause support | | raf/everyRaf.js | Interval timer using requestAnimationFrame | | raf/debounceRaf.js | Smart debounce with optional duration and frame pause | | raf/throttleRaf.js | Input-event throttle using frame-skip control | | raf/waitForRaf.js | Waits for truthy condition using passive frame polling |


License

--{DR.WATT v3.0}--