Skip to content

Conversation

@Seol-JY
Copy link

@Seol-JY Seol-JY commented Nov 22, 2025

Summary

Problem

The original throttle implementation tried to enforce periodic execution by calling debounced.cancel() after throttleMs, but cancel() only cancels without executing. This caused continuous calls to never fire.

Solution

Added maxWait option to debounce to enforce periodic execution, then simplified throttle to use debounce with maxWait: throttleMs.

Implementation Considerations

There were two possible approaches:

Approach Pros Cons
1. Add maxWait to debounce (chosen) Simple throttle implementation, no code duplication Extends debounce public API
2. Implement throttle independently No debounce API change Code duplication, separate logic to maintain

I chose approach 1 because:

  • throttle = debounce + maxWait is a common pattern (lodash does this)
  • Avoids duplicating timer/invocation logic
  • maxWait is a useful option for debounce users as well

However, I'd like maintainer feedback on whether maxWait should be:

  • A public API option (current implementation)
  • An internal-only option (e.g., _maxWait)

Changes

  • src/function/debounce.ts: Added maxWait option to DebounceOptions
  • src/function/throttle.ts: Simplified to use debounce with maxWait
  • src/function/throttle.spec.ts: Added test cases for edges options

Test plan

  • New tests for edges: ['trailing'] and edges: ['leading'] options

Copilot AI review requested due to automatic review settings November 22, 2025 15:13
@vercel
Copy link

vercel bot commented Nov 22, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
es-toolkit Ready Ready Preview Comment Nov 22, 2025 3:35pm

Copilot finished reviewing on behalf of Seol-JY November 22, 2025 15:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where throttle with { edges: ['trailing'] } was behaving like debounce, only firing once at the end instead of periodically during continuous calls. The fix implements throttle functionality by adding a maxWait option to debounce that enforces periodic execution, then simplifying throttle to use debounce with maxWait: throttleMs.

Key changes:

  • Added maxWait option to debounce to enforce maximum delay between invocations
  • Simplified throttle implementation to use debounce with maxWait
  • Added test cases for edges: ['trailing'] and edges: ['leading'] options

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/function/debounce.ts Added maxWait option to enforce periodic execution, including new state tracking (pendingAt) and logic to invoke when max wait time is exceeded
src/function/throttle.ts Simplified from custom implementation to thin wrapper around debounce with maxWait parameter
src/function/throttle.spec.ts Added three new test cases validating throttle behavior with different edge configurations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 165 to 173
if (Date.now() - pendingAt >= maxWait) {
pendingAt = Date.now();
func.apply(pendingThis, pendingArgs);
cancelTimer();
schedule();
return;
}
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The maxWait logic doesn't reset pendingArgs after invoking the function. This could cause the function to be invoked again at the end of the debounce period with the same stale arguments.

After func.apply(pendingThis, pendingArgs) on line 167, you should reset pendingThis and pendingArgs to prevent duplicate invocations:

if (Date.now() - pendingAt >= maxWait) {
  pendingAt = Date.now();
  func.apply(pendingThis, pendingArgs);
  pendingThis = undefined;
  pendingArgs = null;
  cancelTimer();
  schedule();
  return;
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Throttle with custom edges options works just like a debounce.

1 participant