Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onDelay hook #47

Merged
merged 19 commits into from
Dec 7, 2023
Merged
32 changes: 32 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,38 @@ export type Options = {
@default false
*/
readonly strict?: boolean;

/**
sindresorhus marked this conversation as resolved.
Show resolved Hide resolved
Get notified when function calls are delayed due to exceeding the `limit` of allowed calls within the given `interval`.

Can be useful for monitoring the throttling efficiency.

@example
```
sindresorhus marked this conversation as resolved.
Show resolved Hide resolved
import pThrottle from 'p-throttle';

const throttle = pThrottle({
limit: 2,
interval: 1000,
onDelay: () => {
console.log('Reached interval limit, call is delayed');
},
});

const throttled = throttle(() => {
console.log('Executing...');
});

await throttled();
await throttled();
await throttled();
//=> Executing...
//=> Executing...
//=> Reached interval limit, call is delayed
//=> Executing...
```
*/
readonly onDelay?: () => void;
};

/**
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export class AbortError extends Error {
}
}

export default function pThrottle({limit, interval, strict}) {
export default function pThrottle({limit, interval, strict, onDelay}) {
if (!Number.isFinite(limit)) {
throw new TypeError('Expected `limit` to be a finite number');
}
Expand Down Expand Up @@ -84,6 +84,7 @@ export default function pThrottle({limit, interval, strict}) {
if (delay > 0) {
timeoutId = setTimeout(execute, delay);
queue.set(timeoutId, reject);
onDelay?.();
} else {
execute();
}
Expand Down
36 changes: 35 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,47 @@ Type: `number`

The timespan for `limit` in milliseconds.

#### strict
##### strict

Type: `boolean`\
Default: `false`

Use a strict, more resource intensive, throttling algorithm. The default algorithm uses a windowed approach that will work correctly in most cases, limiting the total number of calls at the specified limit per interval window. The strict algorithm throttles each call individually, ensuring the limit is not exceeded for any interval.

##### onDelay

Type: `Function`

Get notified when function calls are delayed due to exceeding the `limit` of allowed calls within the given `interval`.

Can be useful for monitoring the throttling efficiency.

In the following example, the third call gets delayed and triggers the `onDelay` callback:

```js
import pThrottle from 'p-throttle';

const throttle = pThrottle({
limit: 2,
interval: 1000,
onDelay: () => {
console.log('Reached interval limit, call is delayed');
},
});

const throttled = throttle(() => {
console.log('Executing...');
});

await throttled();
await throttled();
await throttled();
//=> Executing...
//=> Executing...
//=> Reached interval limit, call is delayed
//=> Executing...
```

### throttle(function_)

Returns a throttled version of `function_`.
Expand Down
24 changes: 24 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,27 @@ test('manages rapid successive calls', async t => {
await Promise.all(results);
t.pass(); // Test passes if all promises resolve without error
});

test('onDelay', async t => {
let delayedCounter = 0;
const limit = 10;
const interval = 100;
const delayedExecutions = 20;
const onDelay = () => delayedCounter++;
const throttled = pThrottle({limit, interval, onDelay})(() => Date.now());
const promises = [];

for (let index = 0; index < limit; index++) {
promises.push(throttled());
}

t.is(delayedCounter, 0);

for (let index = 0; index < delayedExecutions; index++) {
promises.push(throttled());
}

t.is(delayedCounter, delayedExecutions);

await Promise.all(promises);
});