Fix middle functionality, add end option, migrate tests to fake timers#37
Fix middle functionality, add end option, migrate tests to fake timers#37iansan5653 wants to merge 8 commits into
middle functionality, add end option, migrate tests to fake timers#37Conversation
| expect(calls).toEqual([[1], [2]]) | ||
| vi.advanceTimersByTime(100) // wait for end | ||
|
|
||
| // start: 1; middle: 3; end: 4 | ||
| expect(calls).toEqual([[1], [3], [4]]) |
There was a problem hiding this comment.
This test was incorrect because the functionality was incorrect. The 2 call is a bug; that should be dropped because it is attempted less than 100ms after the 1 call. And the 3 call should have been made but wasn't (because of #23).
So now the rest reflects the correct behavior:
1: First call.startis true so the function is called (same as before)2: It has been less than 100ms since the first call, so this call is dropped3: It has been 100ms since the first call, so the function is called4: It has been less than 100ms since the third call, so this call would be dropped. However, anendcall is scheduled so this call is made after another 100ms
| it('throttle(fn, 100)', async () => { | ||
| await loop(throttle(x => calls.push(x), 100)) | ||
| expect(calls).toEqual([1, 2, 4, 6, 8, 10]) | ||
| it('throttle(fn, 100)', () => { | ||
| loop(throttle(x => calls.push(x), 100)) | ||
| expect(calls).toEqual([1, 3, 5, 7, 9, 10]) | ||
| }) |
There was a problem hiding this comment.
This test was incorrect. The 2 call should be dropped because it's made too fast; then the 3 call should be made since it's done after enough time. So we should land on odd numbers here.
| | fn() | 1 2 3 4 5 6 7 8 9 10 | | ||
| | throttle(fn, 100) | 1 3 5 7 9 10 | | ||
| | throttle(fn, 100, {start: false}) | 3 5 7 9 10 | | ||
| | throttle(fn, 100, {middle: false}) | 1 10 | | ||
| | throttle(fn, 100, {end: false}) | 1 3 5 7 9 | | ||
| | throttle(fn, 100, {once: true}) | 1 | | ||
| | throttle(fn, 100, {once: true, start: false})| 3 | | ||
| | debounce(fn, 100) | 10 | |
There was a problem hiding this comment.
Note these examples now reflect correct behavior; the second call is dropped since the first call is made. And I offset the last column because the end call is made after a short delay.
There was a problem hiding this comment.
Pull request overview
Fixes throttle sequencing, adds trailing-call control, and speeds tests using fake timers.
Changes:
- Refactors throttle timing and adds the
endoption. - Migrates tests from Playwright to Vitest fake timers.
- Updates API documentation and marble diagrams.
Show a summary per file
| File | Description |
|---|---|
index.ts |
Refactors timing and adds end. |
test/index.ts |
Adds fake-timer and end tests. |
README.md |
Documents updated behavior. |
vitest.config.ts |
Disables browser testing. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
README.md:55
- The same
{start:false}timing applies whenonceis enabled: after the fix toonce, the implementation invokes value2on the second attempt and then cancels. The diagram currently promises value3, which is inconsistent with the documented call loop and implementation.
| throttle(fn, 100, {once: true, start: false})| 3 |
- Files reviewed: 4/4 changed files
- Comments generated: 2
- Review effort level: Medium
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
TL;DR: I think we need to explicitly choose between Lodash-style trailing behavior and the burst-end behavior in this PR. The implementation currently chooses burst-end behavior. That is a reasonable model for independently configurable With
The model in this PR appears to be:
That makes Lodash works differently. Its trailing call is tied to the current throttle window, not to inactivity after the latest attempt. It fires with the latest pending arguments at the throttle boundary. If activity continues afterward, that callback effectively becomes a middle callback in retrospect. Lodash therefore does not have a clean, knowable distinction between middle and end at invocation time. This distinction also means the changed expectations from values like The reproduction in #23 also still produces only start and end calls with this implementation: attempts stop at 900ms, before the 1000ms middle threshold, and the end call runs around 1900ms. Lodash would run the pending trailing call around 1000ms. Can we make the intended contract explicit before merging? If burst-end behavior is intentional, I think we should remove the Lodash parity claim, describe |
#23 is occurring because the
middlefunctionality is broken; it only tracks time since last attempt which means that it will never call the function during a sequence.I've fixed this bug and refactored the logic in an attempt to make this more straightforward. While doing so, I also added an option for
endto reach feature parity with Lodash. Note that Lodash calls thistrailingbut I usedendto line up more with how we namedstart(instead ofleading).Finally I updated the tests to use
useFakeTimersand not run in a Playwright browser; this allows them to complete very quickly. And I updated some incorrect test cases (see my PR comments).trailing:false#36