Skip to content

Commit

Permalink
fix: Fix invalid throttle function's behavior
Browse files Browse the repository at this point in the history
- It has to have cooldown state after executing its content.
    - Otherwise, there's a chance to execute it almost simultaneously twice in a row when `leading` is supplied
    - Persistence functionality might have failed because of this issue.
  • Loading branch information
miyanokomiya committed Feb 5, 2024
1 parent eb6dea2 commit d4b73ec
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 22 deletions.
40 changes: 33 additions & 7 deletions src/composables/throttle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,65 @@ describe("newThrottle", () => {
const fn = vi.fn();
const t = newThrottle(fn, 10);
t();
expect(fn).toHaveBeenCalledTimes(0);
await sleep(20);
expect(fn).toHaveBeenCalledTimes(1);
t();
expect(fn).toHaveBeenCalledTimes(1);
await sleep(20);

expect(fn).toHaveBeenCalledTimes(2);
});
test("should pass args", async () => {
test("should pass the latest args", async () => {
const mock = vi.fn();
const fn = (val1: number, val2: number) => mock(val1, val2);
const t = newThrottle(fn, 10);
t(10, 100);
await sleep(5);
t(20, 200);
await sleep(20);
await sleep(6);
expect(mock).toHaveBeenCalledTimes(1);
expect(mock).toHaveBeenCalledWith(20, 200);
t(30, 300);
await sleep(10);
expect(mock).toHaveBeenCalledTimes(2);
expect(mock).toHaveBeenCalledWith(30, 300);
});

describe("if option.leading is true", () => {
describe("when leading is true", () => {
test("should call leading", async () => {
const mock = vi.fn();
const fn = (val1: number, val2: number) => mock(val1, val2);
const t = newThrottle(fn, 10, true);
t(10, 100);
await sleep(20);
expect(mock).toHaveBeenNthCalledWith(1, 10, 100);
await sleep(3);
t(20, 200);
expect(mock).toHaveBeenCalledTimes(1);
await sleep(5);
expect(mock).toHaveBeenCalledTimes(1);
t(30, 300);
await sleep(20);
expect(mock).toHaveBeenCalledTimes(1);
await sleep(5);

expect(mock).toHaveBeenCalledTimes(2);
expect(mock).toHaveBeenNthCalledWith(1, 10, 100);
expect(mock).toHaveBeenNthCalledWith(2, 20, 200);
expect(mock).toHaveBeenNthCalledWith(2, 30, 300);
});

test("should wait for the next execution", async () => {
const mock = vi.fn();
const fn = (val1: number) => mock(val1);
const t = newThrottle(fn, 10, true);
t(10);
expect(mock).toHaveBeenCalledTimes(1);
await sleep(5);
t(20);
expect(mock).toHaveBeenCalledTimes(1);
await sleep(3);
expect(mock).toHaveBeenCalledTimes(1);
await sleep(3);
expect(mock).toHaveBeenCalledTimes(2);
expect(mock).toHaveBeenNthCalledWith(2, 20);
});
});

Expand Down
48 changes: 33 additions & 15 deletions src/composables/throttle.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,75 @@
import { newCallback } from "./reactives";

export function newThrottle<T extends (...args: any[]) => void>(fn: T, interval: number, leading = false) {
let wait = false;
let wait: undefined | "wait" | "cooldown";
let currentArgs: Parameters<T>;
let timer = 0;

function throttle(...args: Parameters<T>) {
currentArgs = args;
callback.dispatch(true);
if (wait) return;

wait = true;
if (wait) {
wait = "wait";
return;
}

if (leading) {
if (leading && !wait) {
fn(...currentArgs);
callback.dispatch(false);
wait = "cooldown";
tick();
} else {
wait = "wait";
tick();
}
}

function tick() {
if (timer) {
clearTimeout(timer);
}

timer = setTimeout(() => {
if (!leading) {
fn(...currentArgs);
}
wait = false;
timer = 0;

if (wait !== "wait") {
wait = undefined;
return;
}

fn(...currentArgs);
wait = "cooldown";
callback.dispatch(false);
tick();
}, interval) as any;
}

throttle.flush = function () {
if (timer) {
clearTimeout(timer);
}
timer = 0;

if (wait) {
if (wait === "wait") {
fn(...currentArgs);
wait = false;
timer = 0;
callback.dispatch(false);
}
wait = undefined;
};

throttle.clear = function (): boolean {
if (timer) {
clearTimeout(timer);
}
timer = 0;

if (wait) {
wait = false;
timer = 0;
if (wait === "wait") {
wait = undefined;
callback.dispatch(false);
return true;
}

wait = undefined;
return false;
};

Expand Down

0 comments on commit d4b73ec

Please sign in to comment.