Skip to content

Commit

Permalink
Add maxWait option (#12)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <[email protected]>
  • Loading branch information
Julusian and sindresorhus authored Feb 28, 2021
1 parent e07dc3f commit 8eb3bcf
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 2 deletions.
11 changes: 10 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
export interface Options {
/**
Time to wait until the `input` function is called.
Time in milliseconds to wait until the `input` function is called.
@default 0
*/
readonly wait?: number;

/**
The maximum time the `input` function is allowed to be delayed before it's invoked.
This can be used to control the rate of calls handled in a constant stream. For example, a media player sending updates every few milliseconds but wants to be handled only once a second.
@default Infinity
*/
readonly maxWait?: number;

/**
Trigger the function on the leading edge of the `wait` interval.
Expand Down
29 changes: 29 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const debounceFn = (inputFunction, options = {}) => {

const {
wait = 0,
maxWait = Number.Infinity,
before = false,
after = true
} = options;
Expand All @@ -16,6 +17,7 @@ const debounceFn = (inputFunction, options = {}) => {
}

let timeout;
let maxTimeout;
let result;

const debouncedFunction = function (...arguments_) {
Expand All @@ -24,6 +26,24 @@ const debounceFn = (inputFunction, options = {}) => {
const later = () => {
timeout = undefined;

if (maxTimeout) {
clearTimeout(maxTimeout);
maxTimeout = undefined;
}

if (after) {
result = inputFunction.apply(context, arguments_);
}
};

const maxLater = () => {
maxTimeout = undefined;

if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}

if (after) {
result = inputFunction.apply(context, arguments_);
}
Expand All @@ -33,6 +53,10 @@ const debounceFn = (inputFunction, options = {}) => {
clearTimeout(timeout);
timeout = setTimeout(later, wait);

if (maxWait > 0 && maxWait !== Number.Infinity && !maxTimeout) {
maxTimeout = setTimeout(maxLater, maxWait);
}

if (shouldCallNow) {
result = inputFunction.apply(context, arguments_);
}
Expand All @@ -47,6 +71,11 @@ const debounceFn = (inputFunction, options = {}) => {
clearTimeout(timeout);
timeout = undefined;
}

if (maxTimeout) {
clearTimeout(maxTimeout);
maxTimeout = undefined;
}
};

return debouncedFunction;
Expand Down
11 changes: 10 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,16 @@ Type: `object`
Type: `number`\
Default: `0`

Time to wait until the `input` function is called.
Time in milliseconds to wait until the `input` function is called.

##### maxWait

Type: `number`\
Default: `Infinity`

The maximum time the `input` function is allowed to be delayed before it's invoked.

This can be used to limit the number of calls handled in a constant stream. For example, a media player sending updates every few milliseconds but wants to be handled only once a second.

##### before

Expand Down
94 changes: 94 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,97 @@ test('.cancel() method', async t => {
t.is(debounced(3), undefined);
t.is(count, 0);
});

test('before:false after:true `maxWait` option', async t => {
let count = 0;

const debounced = debounceFn(value => {
count++;
return value;
}, {
wait: 40,
maxWait: 50,
after: true,
before: false
});

t.is(debounced(1), undefined);
t.is(count, 0);
await delay(30);
t.is(count, 0);

t.is(debounced(2), undefined);
t.is(count, 0);
await delay(30);
t.is(count, 1);

t.is(debounced(3), 1);

t.is(count, 1);
await delay(200);
t.is(count, 2);
});

test('before:true after:false `maxWait` option', async t => {
let count = 0;

const debounced = debounceFn(value => {
count++;
return value;
}, {
wait: 40,
maxWait: 50,
after: false,
before: true
});

t.is(debounced(1), 1);
t.is(count, 1);
await delay(30);

t.is(debounced(2), 1);
t.is(count, 1);
await delay(30);
t.is(count, 1);

t.is(debounced(3), 3);
t.is(count, 2);

await delay(50);
t.is(count, 2);

t.is(debounced(4), 4);
t.is(count, 3);
});

test('before:true after:true `maxWait` option', async t => {
let count = 0;

const debounced = debounceFn(value => {
count++;
return value;
}, {
wait: 40,
maxWait: 50,
after: true,
before: true
});

t.is(debounced(1), 1);
t.is(count, 1);
await delay(30);

t.is(debounced(2), 1);
t.is(count, 1);
await delay(30);
t.is(count, 2);

t.is(debounced(3), 3);
t.is(count, 3);

await delay(50);
t.is(count, 4);

t.is(debounced(4), 4);
t.is(count, 5);
});

0 comments on commit 8eb3bcf

Please sign in to comment.