@chaisser/wait-for
v1.0.0
Published
Promise-based wait: sleep, waitUntil, waitWithTimeout
Maintainers
Readme
⏳ @chaisser/wait-for
Promise-based wait utilities: sleep, waitUntil, retry, debounce, throttle & more
✨ Features
- 🎯 Type-safe - Full TypeScript support with generics
- 😴 Sleep / Delay - Simple promise-based timers
- 🔄 Wait Until - Poll until a condition is true with timeout
- 🔁 Retry - Retry async functions with exponential backoff
- ⏱️ Timeout - Wrap promises with timeout protection
- 🎛️ Debounce & Throttle - Rate-limit function execution
- 📡 Poller - Interval-based polling with start/stop control
- ⏲️ Timer - Pause/resume countdown timer
- 📊 Progress - Track elapsed time on long-running promises
- 🪶 Zero dependencies - Lightweight and tree-shakeable
- 🏎️ ESM + CJS - Dual module format support
📦 Installation
npm install @chaisser/wait-for
# or
yarn add @chaisser/wait-for
# or
pnpm add @chaisser/wait-for🚀 Quick Start
import {
sleep,
waitFor,
waitUntil,
waitWithTimeout,
retry,
debounce,
throttle,
Poller,
Timer,
} from '@chaisser/wait-for';
// Sleep for 1 second
await sleep(1000);
// Wait until a condition is true
await waitUntil(() => document.readyState === 'complete', { timeout: 10000 });
// Retry a flaky operation
const data = await retry(fetchData, { maxAttempts: 3, delay: 100, backoff: 2 });
// Wrap with timeout
const result = await waitWithTimeout(slowOperation(), 5000, 'Too slow!');
// Debounce rapid calls
const debouncedSave = debounce(saveToServer, 300);📖 What It Does
This package provides a collection of async timing utilities for JavaScript and TypeScript. It handles everything from simple delays to complex retry logic with exponential backoff, polling, debouncing, throttling, and timeout-protected promises.
🎯 How It Works
The package is organized into categories:
- Timers -
sleep,waitFor,defer,nextTick - Conditionals -
waitUntil,waitWithTimeout - Retry -
retrywith configurable attempts, delay, and backoff - Rate Limiting -
debounceandthrottle - Polling -
Pollerclass for interval-based callbacks - Countdown -
Timerclass with pause/resume - Composition -
waitForAll,waitForAny,waitForAllWithTimeout,waitForAnyWithTimeout - Progress -
waitWithProgressfor tracking elapsed time
🎨 What It's Useful For
- API Calls - Retry failed requests with backoff
- UI Wait States - Wait until DOM elements appear or state changes
- Rate Limiting - Debounce search inputs, throttle scroll handlers
- Testing - Sleep in tests, wait for async conditions
- Polling - Periodically check for data updates
- Timeouts - Prevent hanging on slow operations
- Timers - Countdown timers with pause/resume support
💡 Usage Examples
Sleep & Delay
import { sleep, waitFor, defer, nextTick } from '@chaisser/wait-for';
// Sleep for 500ms
await sleep(500);
// Alias for sleep
await waitFor(500);
// Defer to next event loop
defer(() => console.log('later'));
// Next tick
nextTick(() => console.log('next'));Wait Until Condition
import { waitUntil } from '@chaisser/wait-for';
// Wait with default settings (50ms interval, 5s timeout)
await waitUntil(() => isReady());
// Custom interval and timeout
await waitUntil(() => db.isConnected(), {
interval: 100,
timeout: 10000,
timeoutMessage: 'Database connection failed'
});Timeout Protection
import { waitWithTimeout } from '@chaisser/wait-for';
// Reject if operation takes longer than 5 seconds
const data = await waitWithTimeout(
fetch('/api/data'),
5000,
'API request timed out'
);Retry with Backoff
import { retry } from '@chaisser/wait-for';
// Retry up to 5 times with exponential backoff
const result = await retry(
() => flakyApiCall(),
{
maxAttempts: 5,
delay: 100, // start at 100ms
backoff: 2 // double each retry: 100, 200, 400, 800
}
);Debounce
import { debounce } from '@chaisser/wait-for';
const debouncedSearch = debounce((query: string) => {
fetchResults(query);
}, 300);
// Only the last call executes after 300ms of silence
debouncedSearch('he');
debouncedSearch('hel');
debouncedSearch('hello'); // only this one firesThrottle
import { throttle } from '@chaisser/wait-for';
const throttledScroll = throttle(() => {
updateScrollPosition();
}, 100);
// Executes at most once per 100ms
window.addEventListener('scroll', throttledScroll);Poller
import { Poller } from '@chaisser/wait-for';
const poller = new Poller(5000);
poller.on(() => {
console.log('Polling for updates...');
checkForUpdates();
});
// Stop after 1 minute
poller.stopAfter(60000);
// Manual control
poller.stop();
poller.start();
poller.isActive(); // true/false
poller.clear(); // remove all callbacksTimer
import { Timer } from '@chaisser/wait-for';
const timer = new Timer(60, (remaining) => {
console.log(`${remaining}s left`);
});
timer.start();
timer.pause();
timer.resume();
timer.stop(); // resets to initial value
timer.getRemaining(); // seconds left
timer.isActive(); // booleanPromise Composition
import {
waitForAll,
waitForAny,
waitForAllWithTimeout,
waitForAnyWithTimeout,
} from '@chaisser/wait-for';
// Wait for all
const results = await waitForAll([
fetchUsers(),
fetchPosts(),
fetchComments(),
]);
// Wait for first
const fastest = await waitForAny([
fetchFromCDN1(),
fetchFromCDN2(),
]);
// With timeout
const data = await waitForAllWithTimeout(
[fetchA(), fetchB()],
5000
);Progress Tracking
import { waitWithProgress } from '@chaisser/wait-for';
const result = await waitWithProgress(
longRunningOperation(),
(elapsed) => {
console.log(`Elapsed: ${elapsed}ms`);
},
500 // report every 500ms
);📚 API Reference
Timers
| Function | Description |
|---|---|
| sleep(ms) | Resolve after specified milliseconds |
| waitFor(ms) | Alias for sleep |
| defer(fn) | Execute function in next event loop |
| nextTick(fn) | Execute function in next tick |
Conditionals
| Function | Description |
|---|---|
| waitUntil(condition, options?) | Poll until condition returns true |
| waitWithTimeout(promise, ms, msg?) | Reject if promise doesn't settle in time |
Retry
| Function | Description |
|---|---|
| retry(fn, options?) | Retry async fn with backoff (maxAttempts, delay, backoff) |
Rate Limiting
| Function | Description |
|---|---|
| debounce(fn, wait) | Delay fn until waitms of silence |
| throttle(fn, wait) | Execute fn at most once per waitms |
Composition
| Function | Description |
|---|---|
| waitForAll(promises) | Promise.all wrapper |
| waitForAny(promises) | Promise.race wrapper |
| waitForAllWithTimeout(promises, ms) | All with timeout |
| waitForAnyWithTimeout(promises, ms) | First with timeout |
| waitWithProgress(promise, onProgress, interval?) | Track elapsed time |
Classes
| Class | Description |
|---|---|
| Poller | Interval-based polling with on(), start(), stop(), clear() |
| Timer | Countdown timer with start(), pause(), resume(), stop() |
🔗 Related Packages
Explore our other utility packages in the @chaisser namespace:
- @chaisser/wait-for (this package) - Promise-based wait utilities
- @chaisser/string-wizard - Advanced string manipulation
- @chaisser/type-guard - Runtime type guards and validators
- @chaisser/uuid-v7 - Time-ordered UUID v7 generator
- @chaisser/human-time - Human-readable time formatting
- @chaisser/obj-path - Object path traversal
- @chaisser/regex-humanizer - Regex pattern explanation
- @chaisser/debounce-throttle - Rate limiting utilities
- @chaisser/color-utils - Color conversion utilities
- @chaisser/deep-clone - Deep cloning functions
- @chaisser/array-group-by - Array grouping utilities
- @chaisser/password-strength - Password strength checker
- @chaisser/merge-objects - Object merge utilities
- @chaisser/chunk-array - Array chunking functions
- @chaisser/event-emitter - Typed event emitter
🔒 License
MIT - Free to use in personal and commercial projects
👨 Developed by
Doruk Karaboncuk [email protected]
📄 Repository
- GitHub: @chaisser
- NPM: @chaisser/wait-for
🤝 Contributing
Contributions are welcome! Feel free to:
- Report bugs
- Suggest new features
- Submit pull requests
- Improve documentation
📞 Support
For issues, questions, or suggestions, please reach out through:
- Email: [email protected]
- GitHub Issues: Create an issue
Made with ❤️ by @chaisser
