
Security News
pnpm 10.12 Introduces Global Virtual Store and Expanded Version Catalogs
pnpm 10.12.1 introduces a global virtual store for faster installs and new options for managing dependencies with version catalogs.
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
pnpm 10.12.1 introduces a global virtual store for faster installs and new options for managing dependencies with version catalogs.
Security News
Amaro 1.0 lays the groundwork for stable TypeScript support in Node.js, bringing official .ts loading closer to reality.
Research
A deceptive PyPI package posing as an Instagram growth tool collects user credentials and sends them to third-party bot services.