UNPKG

timeout-flow

Version:

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

107 lines (91 loc) 2.85 kB
// ./raf/everyRaf.js import { parseDuration } from '../parseDuration.js'; /** * Repeats a function every N milliseconds using requestAnimationFrame * (instead of setTimeout/setInterval). * * @param {string|number} duration - Interval duration ("1s", "500ms", 500, etc.) * @param {Function} fn - Function to run * @param {number} [maxTimes=Infinity] - Max number of executions * @param {boolean} [runImmediately=false] - Whether to run immediately * @returns {Object} - Control methods and flags */ export function everyRaf(duration, fn, maxTimes = Infinity, runImmediately = false) { const ms = typeof duration === 'string' ? parseDuration(duration) : duration; let handle = null; let startTimestamp = null; let lastTimestamp = null; let running = false; let paused = false; let elapsedWhilePaused = 0; let count = 0; const tick = (timestamp) => { if (!running || paused) return; if (startTimestamp == null) { startTimestamp = timestamp; lastTimestamp = timestamp; } const elapsed = timestamp - lastTimestamp + elapsedWhilePaused; if (elapsed >= ms) { if (count >= maxTimes) { cancel(); return; } fn?.(); count++; lastTimestamp = timestamp; elapsedWhilePaused = 0; } handle = requestAnimationFrame(tick); }; const pause = () => { if (running && !paused) { paused = true; elapsedWhilePaused += performance.now() - lastTimestamp; cancelAnimationFrame(handle); } }; const resume = () => { if (running && paused) { paused = false; lastTimestamp = performance.now(); handle = requestAnimationFrame(tick); } }; const cancel = () => { if (handle !== null) { cancelAnimationFrame(handle); handle = null; } running = false; paused = false; startTimestamp = null; lastTimestamp = null; elapsedWhilePaused = 0; }; const reset = (restart = false) => { cancel(); count = 0; if (restart) start(); }; const start = () => { if (runImmediately) { fn?.(); count++; } running = true; paused = false; lastTimestamp = performance.now(); handle = requestAnimationFrame(tick); }; // Initial launch start(); return { pause, resume, cancel, reset, get isRunning() { return running && !paused; }, get count() { return count; } }; }