
Security News
GitHub Actions Pricing Whiplash: Self-Hosted Actions Billing Change Postponed
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.
timeout-flow
Advanced tools
Fluent, composable, pauseable JavaScript timers and time control flows — plus RAF utilities for frame-based logic.
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.
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:
if, while, label, and jumpTo() — not just repetition.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.
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.
npm install timeout-flow
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()
// 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();
import { debounce, throttle, retry, waitFor } from 'timeout-flow';
debounce('300ms', fn) — Run only after silencethrottle('1s', fn) — Run at most once per time windowretry(fn, { attempts, delay, backoff }) — Resilient retry for async callswaitFor(() => condition, { timeout, interval }) — Await condition changeThese 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.
| 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 |
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 | 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 |
--{DR.WATT v3.0}--
FAQs
Fluent, composable, pauseable JavaScript timers and time control flows — plus RAF utilities for frame-based logic.
We found that timeout-flow demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.