Skip to main content

throttle

// A throttle is a common technique used in the browser to improve an application’s performance by limiting the number of events your code needs to handle.
// You should use a throttle when you want to execute a callback at a controlled rate,
// allowing you to handle intermediate states repeatedly at each fixed interval of time.

export default function throttle(
func,
wait,
) {
let throttleTimeout = null;
let storeThis = null;
let storeArgs = null;

function throttleHandler(...args) {
storeArgs = args;
storeThis = this;

const shouldHandleEvent = !throttleTimeout;

if (shouldHandleEvent) {
func.apply(storeThis, storeArgs);

// reset
storeArgs = null;
storeThis = null;

throttleTimeout = setTimeout(() => {
throttleTimeout = null;
if (storeArgs) {
throttleHandler.apply(storeThis, storeArgs);
}
}, wait);
}
}
return throttleHandler;
}


/*
```
import throttle from './throttle';

describe('throttle', () => {
test('can be initialized', () => {
const increment = throttle(() => {}, 50);
expect(increment).toBeInstanceOf(Function);
});

test('invokes callback immediately', () => {
let i = 0;
const increment = throttle(() => {
i++;
}, 50);

expect(i).toBe(0);
increment();
expect(i).toBe(1);
});

test('throttles immediate invocations', () => {
let i = 0;
const increment = throttle(() => {
i++;
}, 50);

expect(i).toBe(0);
increment();
expect(i).toBe(1);
increment();
expect(i).toBe(1);
});

test('throttles delayed invocations', (done) => {
let i = 0;
const increment = throttle(() => {
i++;
}, 100);

expect(i).toBe(0);
increment();
expect(i).toBe(1);

setTimeout(() => {
increment();
expect(i).toBe(1);
}, 25);

setTimeout(() => {
increment();
expect(i).toBe(1);
done();
}, 50);
});

test('uses arguments', () => {
let i = 21;
const increment = throttle((a, b) => {
i += a * b;
}, 50);

expect(i).toBe(21);
increment(3, 7);
expect(i).toBe(42);
});

test('can be called again after first throttling window', (done) => {
let i = 0;
const increment = throttle(() => {
i++;
}, 100);

expect(i).toBe(0);
increment();
expect(i).toBe(1);

// Should not fire yet.
setTimeout(() => {
expect(i).toBe(1);
increment();
expect(i).toBe(1);
}, 50);

setTimeout(() => {
expect(i).toBe(1);
increment();
expect(i).toBe(2);
}, 150);

setTimeout(() => {
expect(i).toBe(2);
increment();
expect(i).toBe(2);
done();
}, 200);
});

test('callbacks can access `this`', (done) => {
const increment = throttle(function (delta) {
this.val += delta;
}, 50);

const obj = {
val: 2,
increment,
};

expect(obj.val).toBe(2);
obj.increment(3);
expect(obj.val).toBe(5);

setTimeout(() => {
obj.increment(10);
expect(obj.val).toBe(15);
done();
}, 100);
});
});
```
*/