Problem Statement
In React applications and complex UIs, we need the ability to cancel pending throttled operations (e.g., on component unmount) and flush pending calls immediately (e.g., before navigation). Implement a throttle with full lifecycle control.
Requirements
Implement a throttle(func, wait) function that returns a throttled function with:
cancel()- cancels any pending trailing invocationflush()- immediately executes pending trailing invocation (if any)pending()- returns boolean indicating if a trailing call is pending- Both leading and trailing enabled by default
Example Usage
const syncToServer = (data) => {
console.log(`Syncing: ${JSON.stringify(data)}`);
};
const throttledSync = throttle(syncToServer, 2000);
// User makes rapid changes
throttledSync({ v: 1 }); // Executes immediately (leading)
throttledSync({ v: 2 }); // Queued for trailing
throttledSync({ v: 3 }); // Replaces queue
console.log(throttledSync.pending()); // true
// Option 1: Cancel - discard pending
throttledSync.cancel();
console.log(throttledSync.pending()); // false
// Nothing syncs at t=2000
// Option 2: Flush - execute immediately
throttledSync({ v: 4 }); // Executes immediately
throttledSync({ v: 5 }); // Queued
throttledSync.flush(); // Immediately logs: "Syncing: {"v":5}"
// React cleanup pattern
useEffect(() => {
const throttled = throttle(handleScroll, 100);
window.addEventListener("scroll", throttled);
return () => {
throttled.flush(); // Ensure final state is captured
throttled.cancel(); // Then cleanup
window.removeEventListener("scroll", throttled);
};
}, []);Implementation Considerations
- flush() should be idempotent if nothing is pending
- cancel() should not affect the next leading call
- Methods should be safely callable at any time
Follow-up Questions
- Should flush() return the result of the function call?
- How do you handle the case where flush() is called during execution?