@common.js/p-throttle 6.2.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/index.d.ts +91 -18
- package/index.js +287 -207
- package/package.json +22 -8
package/README.md
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
The [p-throttle](https://www.npmjs.com/package/p-throttle) package exported as CommonJS modules.
|
|
4
4
|
|
|
5
|
-
Exported from [p-throttle@
|
|
5
|
+
Exported from [p-throttle@8.1.0](https://www.npmjs.com/package/p-throttle/v/8.1.0) using https://github.com/etienne-martin/common.js.
|
package/index.d.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
export class AbortError extends Error {
|
|
2
|
-
readonly name: 'AbortError';
|
|
3
|
-
|
|
4
|
-
private constructor();
|
|
5
|
-
}
|
|
6
|
-
|
|
7
1
|
type AnyFunction = (...arguments_: readonly any[]) => unknown;
|
|
8
2
|
|
|
9
3
|
export type ThrottledFunction<F extends AnyFunction> = F & {
|
|
10
4
|
/**
|
|
11
|
-
Whether future function calls should be throttled
|
|
5
|
+
Whether future function calls should be throttled and count towards throttling thresholds.
|
|
12
6
|
|
|
13
7
|
@default true
|
|
14
8
|
*/
|
|
@@ -16,13 +10,28 @@ export type ThrottledFunction<F extends AnyFunction> = F & {
|
|
|
16
10
|
|
|
17
11
|
/**
|
|
18
12
|
The number of queued items waiting to be executed.
|
|
19
|
-
*/
|
|
20
|
-
readonly queueSize: number;
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
This can be useful for implementing queue management strategies, such as using a fallback when the queue is too full.
|
|
15
|
+
|
|
16
|
+
@example
|
|
17
|
+
```
|
|
18
|
+
import pThrottle from 'p-throttle';
|
|
19
|
+
|
|
20
|
+
const throttle = pThrottle({limit: 1, interval: 1000});
|
|
21
|
+
|
|
22
|
+
const accurateData = throttle(() => fetch('https://accurate-api.example.com'));
|
|
23
|
+
const roughData = () => fetch('https://rough-api.example.com');
|
|
24
|
+
|
|
25
|
+
async function getData() {
|
|
26
|
+
if (accurateData.queueSize >= 3) {
|
|
27
|
+
return roughData(); // Queue full, use fallback
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return accurateData();
|
|
31
|
+
}
|
|
32
|
+
```
|
|
24
33
|
*/
|
|
25
|
-
|
|
34
|
+
readonly queueSize: number;
|
|
26
35
|
};
|
|
27
36
|
|
|
28
37
|
export type Options = {
|
|
@@ -37,16 +46,46 @@ export type Options = {
|
|
|
37
46
|
readonly interval: number;
|
|
38
47
|
|
|
39
48
|
/**
|
|
40
|
-
Use a strict, more resource
|
|
49
|
+
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.
|
|
41
50
|
|
|
42
51
|
@default false
|
|
43
52
|
*/
|
|
44
53
|
readonly strict?: boolean;
|
|
45
54
|
|
|
46
55
|
/**
|
|
47
|
-
|
|
56
|
+
Abort pending executions. When aborted, all unresolved promises are rejected with `signal.reason`.
|
|
57
|
+
|
|
58
|
+
@example
|
|
59
|
+
```
|
|
60
|
+
import pThrottle from 'p-throttle';
|
|
61
|
+
|
|
62
|
+
const controller = new AbortController();
|
|
48
63
|
|
|
49
|
-
|
|
64
|
+
const throttle = pThrottle({
|
|
65
|
+
limit: 2,
|
|
66
|
+
interval: 1000,
|
|
67
|
+
signal: controller.signal
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const throttled = throttle(() => {
|
|
71
|
+
console.log('Executing...');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await throttled();
|
|
75
|
+
await throttled();
|
|
76
|
+
controller.abort('aborted');
|
|
77
|
+
await throttled();
|
|
78
|
+
//=> Executing...
|
|
79
|
+
//=> Executing...
|
|
80
|
+
//=> Promise rejected with reason `aborted`
|
|
81
|
+
```
|
|
82
|
+
*/
|
|
83
|
+
signal?: AbortSignal;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
Get notified when function calls are delayed due to exceeding the `limit` of allowed calls within the given `interval`.
|
|
87
|
+
|
|
88
|
+
The delayed call arguments are passed to the `onDelay` callback. Can be useful for monitoring the throttling efficiency.
|
|
50
89
|
|
|
51
90
|
@example
|
|
52
91
|
```
|
|
@@ -61,8 +100,8 @@ export type Options = {
|
|
|
61
100
|
});
|
|
62
101
|
|
|
63
102
|
const throttled = throttle((a, b) => {
|
|
64
|
-
|
|
65
|
-
|
|
103
|
+
console.log(`Executing with ${a} ${b}...`);
|
|
104
|
+
});
|
|
66
105
|
|
|
67
106
|
await throttled(1, 2);
|
|
68
107
|
await throttled(3, 4);
|
|
@@ -74,10 +113,44 @@ export type Options = {
|
|
|
74
113
|
```
|
|
75
114
|
*/
|
|
76
115
|
readonly onDelay?: (...arguments_: readonly any[]) => void;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
Calculate the weight/cost of each function call based on its arguments.
|
|
119
|
+
|
|
120
|
+
The weight determines how much of the `limit` is consumed by each call. This is useful for rate limiting APIs that use point-based or cost-based limits, where different operations consume different amounts of the quota.
|
|
121
|
+
|
|
122
|
+
By default, each call has a weight of `1`.
|
|
123
|
+
|
|
124
|
+
In the following example, queries with different numbers of tables consume different amounts of the rate limit:
|
|
125
|
+
|
|
126
|
+
@example
|
|
127
|
+
```
|
|
128
|
+
import pThrottle from 'p-throttle';
|
|
129
|
+
|
|
130
|
+
// Storyblok GraphQL API: 100 points per second
|
|
131
|
+
// Each query costs 1 point for the connection plus 1 point per table
|
|
132
|
+
const throttle = pThrottle({
|
|
133
|
+
limit: 100,
|
|
134
|
+
interval: 1000,
|
|
135
|
+
weight: numberOfTables => 1 + numberOfTables
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const fetchData = throttle(numberOfTables => {
|
|
139
|
+
// Fetch GraphQL data
|
|
140
|
+
return fetch('...');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await fetchData(1); // Costs 2 points
|
|
144
|
+
await fetchData(3); // Costs 4 points
|
|
145
|
+
```
|
|
146
|
+
*/
|
|
147
|
+
readonly weight?: (...arguments_: readonly any[]) => number;
|
|
77
148
|
};
|
|
78
149
|
|
|
79
150
|
/**
|
|
80
|
-
Throttle promise-returning
|
|
151
|
+
Throttle promise-returning & async functions.
|
|
152
|
+
|
|
153
|
+
Also works with normal functions.
|
|
81
154
|
|
|
82
155
|
It rate-limits function calls without discarding them, making it ideal for external API interactions where avoiding call loss is crucial.
|
|
83
156
|
|
package/index.js
CHANGED
|
@@ -2,17 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", {
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
get: all[name]
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
AbortError: function() {
|
|
13
|
-
return AbortError;
|
|
14
|
-
},
|
|
15
|
-
default: function() {
|
|
5
|
+
Object.defineProperty(exports, "default", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
16
8
|
return pThrottle;
|
|
17
9
|
}
|
|
18
10
|
});
|
|
@@ -24,12 +16,6 @@ function _arrayLikeToArray(arr, len) {
|
|
|
24
16
|
function _arrayWithoutHoles(arr) {
|
|
25
17
|
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
|
26
18
|
}
|
|
27
|
-
function _assertThisInitialized(self) {
|
|
28
|
-
if (self === void 0) {
|
|
29
|
-
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
|
30
|
-
}
|
|
31
|
-
return self;
|
|
32
|
-
}
|
|
33
19
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
34
20
|
try {
|
|
35
21
|
var info = gen[key](arg);
|
|
@@ -59,87 +45,15 @@ function _asyncToGenerator(fn) {
|
|
|
59
45
|
});
|
|
60
46
|
};
|
|
61
47
|
}
|
|
62
|
-
function _classCallCheck(instance, Constructor) {
|
|
63
|
-
if (!(instance instanceof Constructor)) {
|
|
64
|
-
throw new TypeError("Cannot call a class as a function");
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
function isNativeReflectConstruct() {
|
|
68
|
-
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
69
|
-
if (Reflect.construct.sham) return false;
|
|
70
|
-
if (typeof Proxy === "function") return true;
|
|
71
|
-
try {
|
|
72
|
-
Date.prototype.toString.call(Reflect.construct(Date, [], function() {}));
|
|
73
|
-
return true;
|
|
74
|
-
} catch (e) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function _construct(Parent, args, Class) {
|
|
79
|
-
if (isNativeReflectConstruct()) {
|
|
80
|
-
_construct = Reflect.construct;
|
|
81
|
-
} else {
|
|
82
|
-
_construct = function _construct(Parent, args, Class) {
|
|
83
|
-
var a = [
|
|
84
|
-
null
|
|
85
|
-
];
|
|
86
|
-
a.push.apply(a, args);
|
|
87
|
-
var Constructor = Function.bind.apply(Parent, a);
|
|
88
|
-
var instance = new Constructor();
|
|
89
|
-
if (Class) _setPrototypeOf(instance, Class.prototype);
|
|
90
|
-
return instance;
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
return _construct.apply(null, arguments);
|
|
94
|
-
}
|
|
95
|
-
function _getPrototypeOf(o) {
|
|
96
|
-
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
|
|
97
|
-
return o.__proto__ || Object.getPrototypeOf(o);
|
|
98
|
-
};
|
|
99
|
-
return _getPrototypeOf(o);
|
|
100
|
-
}
|
|
101
|
-
function _inherits(subClass, superClass) {
|
|
102
|
-
if (typeof superClass !== "function" && superClass !== null) {
|
|
103
|
-
throw new TypeError("Super expression must either be null or a function");
|
|
104
|
-
}
|
|
105
|
-
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
|
106
|
-
constructor: {
|
|
107
|
-
value: subClass,
|
|
108
|
-
writable: true,
|
|
109
|
-
configurable: true
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
if (superClass) _setPrototypeOf(subClass, superClass);
|
|
113
|
-
}
|
|
114
|
-
function _isNativeFunction(fn) {
|
|
115
|
-
return Function.toString.call(fn).indexOf("[native code]") !== -1;
|
|
116
|
-
}
|
|
117
48
|
function _iterableToArray(iter) {
|
|
118
49
|
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
119
50
|
}
|
|
120
51
|
function _nonIterableSpread() {
|
|
121
52
|
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
122
53
|
}
|
|
123
|
-
function _possibleConstructorReturn(self, call) {
|
|
124
|
-
if (call && (_typeof(call) === "object" || typeof call === "function")) {
|
|
125
|
-
return call;
|
|
126
|
-
}
|
|
127
|
-
return _assertThisInitialized(self);
|
|
128
|
-
}
|
|
129
|
-
function _setPrototypeOf(o, p) {
|
|
130
|
-
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
|
131
|
-
o.__proto__ = p;
|
|
132
|
-
return o;
|
|
133
|
-
};
|
|
134
|
-
return _setPrototypeOf(o, p);
|
|
135
|
-
}
|
|
136
54
|
function _toConsumableArray(arr) {
|
|
137
55
|
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
|
138
56
|
}
|
|
139
|
-
var _typeof = function(obj) {
|
|
140
|
-
"@swc/helpers - typeof";
|
|
141
|
-
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
142
|
-
};
|
|
143
57
|
function _unsupportedIterableToArray(o, minLen) {
|
|
144
58
|
if (!o) return;
|
|
145
59
|
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
|
@@ -148,56 +62,6 @@ function _unsupportedIterableToArray(o, minLen) {
|
|
|
148
62
|
if (n === "Map" || n === "Set") return Array.from(n);
|
|
149
63
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
|
150
64
|
}
|
|
151
|
-
function _wrapNativeSuper(Class) {
|
|
152
|
-
var _cache = typeof Map === "function" ? new Map() : undefined;
|
|
153
|
-
_wrapNativeSuper = function _wrapNativeSuper(Class) {
|
|
154
|
-
if (Class === null || !_isNativeFunction(Class)) return Class;
|
|
155
|
-
if (typeof Class !== "function") {
|
|
156
|
-
throw new TypeError("Super expression must either be null or a function");
|
|
157
|
-
}
|
|
158
|
-
if (typeof _cache !== "undefined") {
|
|
159
|
-
if (_cache.has(Class)) return _cache.get(Class);
|
|
160
|
-
_cache.set(Class, Wrapper);
|
|
161
|
-
}
|
|
162
|
-
function Wrapper() {
|
|
163
|
-
return _construct(Class, arguments, _getPrototypeOf(this).constructor);
|
|
164
|
-
}
|
|
165
|
-
Wrapper.prototype = Object.create(Class.prototype, {
|
|
166
|
-
constructor: {
|
|
167
|
-
value: Wrapper,
|
|
168
|
-
enumerable: false,
|
|
169
|
-
writable: true,
|
|
170
|
-
configurable: true
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
return _setPrototypeOf(Wrapper, Class);
|
|
174
|
-
};
|
|
175
|
-
return _wrapNativeSuper(Class);
|
|
176
|
-
}
|
|
177
|
-
function _isNativeReflectConstruct() {
|
|
178
|
-
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
|
179
|
-
if (Reflect.construct.sham) return false;
|
|
180
|
-
if (typeof Proxy === "function") return true;
|
|
181
|
-
try {
|
|
182
|
-
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function() {}));
|
|
183
|
-
return true;
|
|
184
|
-
} catch (e) {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
function _createSuper(Derived) {
|
|
189
|
-
var hasNativeReflectConstruct = _isNativeReflectConstruct();
|
|
190
|
-
return function _createSuperInternal() {
|
|
191
|
-
var Super = _getPrototypeOf(Derived), result;
|
|
192
|
-
if (hasNativeReflectConstruct) {
|
|
193
|
-
var NewTarget = _getPrototypeOf(this).constructor;
|
|
194
|
-
result = Reflect.construct(Super, arguments, NewTarget);
|
|
195
|
-
} else {
|
|
196
|
-
result = Super.apply(this, arguments);
|
|
197
|
-
}
|
|
198
|
-
return _possibleConstructorReturn(this, result);
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
65
|
var __generator = (void 0) && (void 0).__generator || function(thisArg, body) {
|
|
202
66
|
var f, y, t, g, _ = {
|
|
203
67
|
label: 0,
|
|
@@ -293,54 +157,142 @@ var __generator = (void 0) && (void 0).__generator || function(thisArg, body) {
|
|
|
293
157
|
};
|
|
294
158
|
}
|
|
295
159
|
};
|
|
296
|
-
var
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
var
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
160
|
+
var states = new WeakMap();
|
|
161
|
+
var signalThrottleds = new WeakMap(); // AbortSignal -> {throttleds: Set<WeakRef>, listener: Function}
|
|
162
|
+
var finalizationRegistry = new FinalizationRegistry(function(param) {
|
|
163
|
+
var signalWeakRef = param.signalWeakRef, weakReference = param.weakReference;
|
|
164
|
+
var signal = signalWeakRef.deref();
|
|
165
|
+
if (!signal) {
|
|
166
|
+
return; // Signal already GC'd
|
|
167
|
+
}
|
|
168
|
+
var registration = signalThrottleds.get(signal);
|
|
169
|
+
if (registration) {
|
|
170
|
+
registration.throttleds.delete(weakReference);
|
|
171
|
+
if (registration.throttleds.size === 0) {
|
|
172
|
+
// Remove the abort listener when no throttleds remain
|
|
173
|
+
signal.removeEventListener("abort", registration.listener);
|
|
174
|
+
signalThrottleds.delete(signal);
|
|
175
|
+
}
|
|
306
176
|
}
|
|
307
|
-
|
|
308
|
-
}(_wrapNativeSuper(Error));
|
|
177
|
+
});
|
|
309
178
|
function pThrottle(param) {
|
|
310
|
-
var limit = param.limit, interval = param.interval, strict = param.strict, onDelay = param.onDelay;
|
|
311
|
-
var windowedDelay = function windowedDelay() {
|
|
179
|
+
var limit = param.limit, interval = param.interval, strict = param.strict, signal = param.signal, onDelay = param.onDelay, weight = param.weight;
|
|
180
|
+
var windowedDelay = function windowedDelay(requestWeight) {
|
|
312
181
|
var now = Date.now();
|
|
313
|
-
if (now - currentTick > interval) {
|
|
314
|
-
|
|
315
|
-
currentTick = now;
|
|
182
|
+
if (now - state.currentTick > interval) {
|
|
183
|
+
state.activeWeight = requestWeight;
|
|
184
|
+
state.currentTick = now;
|
|
316
185
|
return 0;
|
|
317
186
|
}
|
|
318
|
-
if (
|
|
319
|
-
|
|
187
|
+
if (state.activeWeight + requestWeight <= limit) {
|
|
188
|
+
state.activeWeight += requestWeight;
|
|
320
189
|
} else {
|
|
321
|
-
currentTick += interval;
|
|
322
|
-
|
|
190
|
+
state.currentTick += interval;
|
|
191
|
+
state.activeWeight = requestWeight;
|
|
323
192
|
}
|
|
324
|
-
return currentTick - now;
|
|
193
|
+
return state.currentTick - now;
|
|
325
194
|
};
|
|
326
|
-
var strictDelay = function strictDelay() {
|
|
195
|
+
var strictDelay = function strictDelay(requestWeight) {
|
|
327
196
|
var now = Date.now();
|
|
328
197
|
// Clear the queue if there's a significant delay since the last execution
|
|
329
|
-
if (strictTicks.length > 0 && now - strictTicks.at(-1) > interval) {
|
|
330
|
-
strictTicks.length = 0;
|
|
198
|
+
if (state.strictTicks.length > 0 && now - state.strictTicks.at(-1).time > interval) {
|
|
199
|
+
state.strictTicks.length = 0;
|
|
331
200
|
}
|
|
332
|
-
//
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
|
|
201
|
+
// For weighted throttling, use time-based sliding window
|
|
202
|
+
if (weight) {
|
|
203
|
+
// Remove ticks outside the current interval window
|
|
204
|
+
while(state.strictTicks.length > 0 && now - state.strictTicks[0].time >= interval){
|
|
205
|
+
state.strictTicks.shift();
|
|
206
|
+
}
|
|
207
|
+
var weightInWindowAt = function(time) {
|
|
208
|
+
var total = 0;
|
|
209
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
210
|
+
try {
|
|
211
|
+
for(var _iterator = state.strictTicks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
212
|
+
var tick = _step.value;
|
|
213
|
+
if (tick.time <= time && time - tick.time < interval) {
|
|
214
|
+
total += tick.weight;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch (err) {
|
|
218
|
+
_didIteratorError = true;
|
|
219
|
+
_iteratorError = err;
|
|
220
|
+
} finally{
|
|
221
|
+
try {
|
|
222
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
223
|
+
_iterator.return();
|
|
224
|
+
}
|
|
225
|
+
} finally{
|
|
226
|
+
if (_didIteratorError) {
|
|
227
|
+
throw _iteratorError;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return total;
|
|
232
|
+
};
|
|
233
|
+
// Execute immediately if capacity available
|
|
234
|
+
if (weightInWindowAt(now) + requestWeight <= limit) {
|
|
235
|
+
var tickRecord = {
|
|
236
|
+
time: now,
|
|
237
|
+
weight: requestWeight
|
|
238
|
+
};
|
|
239
|
+
insertTickSorted(tickRecord);
|
|
240
|
+
return {
|
|
241
|
+
delay: 0
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// Find earliest time when window will have room
|
|
245
|
+
var nextExecutionTime = now;
|
|
246
|
+
while(weightInWindowAt(nextExecutionTime) + requestWeight > limit){
|
|
247
|
+
var firstInWindow = state.strictTicks.find(function(tick) {
|
|
248
|
+
return tick.time <= nextExecutionTime && nextExecutionTime - tick.time < interval;
|
|
249
|
+
});
|
|
250
|
+
if (!firstInWindow) {
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
nextExecutionTime = firstInWindow.time + interval;
|
|
254
|
+
}
|
|
255
|
+
var tickRecord1 = {
|
|
256
|
+
time: nextExecutionTime,
|
|
257
|
+
weight: requestWeight
|
|
258
|
+
};
|
|
259
|
+
insertTickSorted(tickRecord1);
|
|
260
|
+
return {
|
|
261
|
+
delay: Math.max(0, nextExecutionTime - now),
|
|
262
|
+
tickRecord: tickRecord1
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
// For non-weighted throttling, use count-based queue (original algorithm)
|
|
266
|
+
// If the queue is not full (treat limit 0 as capacity 1 for seeding), add the current time and execute immediately
|
|
267
|
+
if (state.strictTicks.length < strictCapacity) {
|
|
268
|
+
state.strictTicks.push({
|
|
269
|
+
time: now,
|
|
270
|
+
weight: requestWeight
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
delay: 0
|
|
274
|
+
};
|
|
336
275
|
}
|
|
337
|
-
// Calculate
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
strictTicks.
|
|
341
|
-
|
|
276
|
+
// Calculate next execution time: must be after oldest + interval,
|
|
277
|
+
// AND must be after the most recent to prevent multiple calls bunching up
|
|
278
|
+
var oldestTime = state.strictTicks[0].time;
|
|
279
|
+
var mostRecentTime = state.strictTicks.at(-1).time;
|
|
280
|
+
var baseTime = oldestTime + interval;
|
|
281
|
+
// Add minimum spacing to prevent bunching (except for interval=0)
|
|
282
|
+
var minSpacing = interval > 0 ? Math.ceil(interval / strictCapacity) : 0;
|
|
283
|
+
var nextExecutionTime1 = baseTime <= mostRecentTime ? mostRecentTime + minSpacing : baseTime;
|
|
284
|
+
// Shift the queue and add a record for the new execution
|
|
285
|
+
state.strictTicks.shift();
|
|
286
|
+
var tickRecord2 = {
|
|
287
|
+
time: nextExecutionTime1,
|
|
288
|
+
weight: requestWeight
|
|
289
|
+
};
|
|
290
|
+
state.strictTicks.push(tickRecord2);
|
|
342
291
|
// Calculate the delay for the current execution
|
|
343
|
-
return
|
|
292
|
+
return {
|
|
293
|
+
delay: Math.max(0, nextExecutionTime1 - now),
|
|
294
|
+
tickRecord: tickRecord2
|
|
295
|
+
};
|
|
344
296
|
};
|
|
345
297
|
if (!Number.isFinite(limit)) {
|
|
346
298
|
throw new TypeError("Expected `limit` to be a finite number");
|
|
@@ -348,10 +300,46 @@ function pThrottle(param) {
|
|
|
348
300
|
if (!Number.isFinite(interval)) {
|
|
349
301
|
throw new TypeError("Expected `interval` to be a finite number");
|
|
350
302
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
303
|
+
if (limit < 0) {
|
|
304
|
+
throw new TypeError("Expected `limit` to be >= 0");
|
|
305
|
+
}
|
|
306
|
+
if (interval < 0) {
|
|
307
|
+
throw new TypeError("Expected `interval` to be >= 0");
|
|
308
|
+
}
|
|
309
|
+
if (weight !== undefined && typeof weight !== "function") {
|
|
310
|
+
throw new TypeError("Expected `weight` to be a function");
|
|
311
|
+
}
|
|
312
|
+
if (weight && interval === 0) {
|
|
313
|
+
throw new TypeError("The `weight` option cannot be used with `interval` of 0");
|
|
314
|
+
}
|
|
315
|
+
// TODO: Uncomment in next major version (breaking change)
|
|
316
|
+
// The combination of strict mode with interval=0 doesn't enforce the limit correctly
|
|
317
|
+
// (minSpacing becomes 0, allowing unlimited calls in the same millisecond).
|
|
318
|
+
// For now, we allow it to avoid breaking existing code, but it should be rejected in v9.
|
|
319
|
+
// if (strict && interval === 0) {
|
|
320
|
+
// throw new TypeError('The `strict` option cannot be used with `interval` of 0');
|
|
321
|
+
// }
|
|
322
|
+
var state = {
|
|
323
|
+
queue: new Map(),
|
|
324
|
+
strictTicks: [],
|
|
325
|
+
// Track windowed algorithm state so it can be reset on abort
|
|
326
|
+
currentTick: 0,
|
|
327
|
+
activeWeight: 0
|
|
328
|
+
};
|
|
329
|
+
var strictCapacity = Math.max(limit, 1);
|
|
330
|
+
// Helper: insert tick maintaining sorted order by time (for weighted strict mode)
|
|
331
|
+
var insertTickSorted = function(tickRecord) {
|
|
332
|
+
// Optimization: append if it belongs at the end (common case - O(1))
|
|
333
|
+
if (state.strictTicks.length === 0 || tickRecord.time >= state.strictTicks.at(-1).time) {
|
|
334
|
+
state.strictTicks.push(tickRecord);
|
|
335
|
+
} else {
|
|
336
|
+
// Insert at correct position (rare case - O(n))
|
|
337
|
+
var insertIndex = state.strictTicks.findIndex(function(tick) {
|
|
338
|
+
return tick.time > tickRecord.time;
|
|
339
|
+
});
|
|
340
|
+
state.strictTicks.splice(insertIndex, 0, tickRecord);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
355
343
|
var getDelay = strict ? strictDelay : windowedDelay;
|
|
356
344
|
return function(function_) {
|
|
357
345
|
var throttled = function() {
|
|
@@ -372,51 +360,143 @@ function pThrottle(param) {
|
|
|
372
360
|
}
|
|
373
361
|
var timeoutId;
|
|
374
362
|
return new Promise(function(resolve, reject) {
|
|
363
|
+
// Calculate weight for this call
|
|
364
|
+
var requestWeight = 1;
|
|
365
|
+
if (weight) {
|
|
366
|
+
try {
|
|
367
|
+
requestWeight = weight.apply(void 0, _toConsumableArray(arguments_));
|
|
368
|
+
} catch (error) {
|
|
369
|
+
reject(error);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
// Validate weight
|
|
373
|
+
if (!Number.isFinite(requestWeight) || requestWeight < 0) {
|
|
374
|
+
reject(new TypeError("Expected `weight` to be a finite non-negative number"));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (requestWeight > limit) {
|
|
378
|
+
reject(new TypeError("Expected `weight` (".concat(requestWeight, ") to be <= `limit` (").concat(limit, ")")));
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
var delayResult = getDelay(requestWeight);
|
|
383
|
+
var delay = strict ? delayResult.delay : delayResult;
|
|
384
|
+
var tickRecord = strict ? delayResult.tickRecord : undefined;
|
|
375
385
|
var execute = function() {
|
|
376
|
-
|
|
377
|
-
|
|
386
|
+
// Update strictTicks with actual execution time to account for setTimeout drift
|
|
387
|
+
if (tickRecord) {
|
|
388
|
+
var actualTime = Date.now();
|
|
389
|
+
// For weighted throttling with drift, maintain sorted order
|
|
390
|
+
if (weight && tickRecord.time !== actualTime) {
|
|
391
|
+
tickRecord.time = actualTime;
|
|
392
|
+
var index = state.strictTicks.indexOf(tickRecord);
|
|
393
|
+
state.strictTicks.splice(index, 1);
|
|
394
|
+
insertTickSorted(tickRecord);
|
|
395
|
+
} else {
|
|
396
|
+
tickRecord.time = actualTime;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
try {
|
|
400
|
+
resolve(function_.apply(_this, arguments_));
|
|
401
|
+
} catch (error) {
|
|
402
|
+
reject(error);
|
|
403
|
+
}
|
|
404
|
+
state.queue.delete(timeoutId);
|
|
378
405
|
};
|
|
379
|
-
var delay = getDelay();
|
|
380
406
|
if (delay > 0) {
|
|
381
407
|
timeoutId = setTimeout(execute, delay);
|
|
382
|
-
queue.set(timeoutId, reject);
|
|
383
|
-
|
|
408
|
+
state.queue.set(timeoutId, reject);
|
|
409
|
+
try {
|
|
410
|
+
onDelay === null || onDelay === void 0 ? void 0 : onDelay.apply(void 0, _toConsumableArray(arguments_));
|
|
411
|
+
} catch (e) {}
|
|
384
412
|
} else {
|
|
385
413
|
execute();
|
|
386
414
|
}
|
|
387
415
|
});
|
|
388
416
|
};
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
417
|
+
signal === null || signal === void 0 ? void 0 : signal.throwIfAborted();
|
|
418
|
+
if (signal) {
|
|
419
|
+
var registration = signalThrottleds.get(signal);
|
|
420
|
+
if (!registration) {
|
|
421
|
+
registration = {
|
|
422
|
+
throttleds: new Set(),
|
|
423
|
+
listener: null
|
|
424
|
+
};
|
|
425
|
+
registration.listener = function() {
|
|
426
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
427
|
+
try {
|
|
428
|
+
for(var _iterator = registration.throttleds[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
429
|
+
var weakReference = _step.value;
|
|
430
|
+
var function_ = weakReference.deref();
|
|
431
|
+
if (!function_) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
var functionState = states.get(function_);
|
|
435
|
+
if (!functionState) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
439
|
+
try {
|
|
440
|
+
for(var _iterator1 = functionState.queue.keys()[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
441
|
+
var timeout = _step1.value;
|
|
442
|
+
clearTimeout(timeout);
|
|
443
|
+
functionState.queue.get(timeout)(signal.reason);
|
|
444
|
+
}
|
|
445
|
+
} catch (err) {
|
|
446
|
+
_didIteratorError1 = true;
|
|
447
|
+
_iteratorError1 = err;
|
|
448
|
+
} finally{
|
|
449
|
+
try {
|
|
450
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
451
|
+
_iterator1.return();
|
|
452
|
+
}
|
|
453
|
+
} finally{
|
|
454
|
+
if (_didIteratorError1) {
|
|
455
|
+
throw _iteratorError1;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
functionState.queue.clear();
|
|
460
|
+
functionState.strictTicks.length = 0;
|
|
461
|
+
// Reset windowed state so subsequent calls are not artificially delayed
|
|
462
|
+
functionState.currentTick = 0;
|
|
463
|
+
functionState.activeWeight = 0;
|
|
464
|
+
}
|
|
465
|
+
} catch (err) {
|
|
466
|
+
_didIteratorError = true;
|
|
467
|
+
_iteratorError = err;
|
|
468
|
+
} finally{
|
|
469
|
+
try {
|
|
470
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
471
|
+
_iterator.return();
|
|
472
|
+
}
|
|
473
|
+
} finally{
|
|
474
|
+
if (_didIteratorError) {
|
|
475
|
+
throw _iteratorError;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
408
478
|
}
|
|
409
|
-
|
|
479
|
+
signalThrottleds.delete(signal);
|
|
480
|
+
};
|
|
481
|
+
signalThrottleds.set(signal, registration);
|
|
482
|
+
signal.addEventListener("abort", registration.listener, {
|
|
483
|
+
once: true
|
|
484
|
+
});
|
|
410
485
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
486
|
+
var weakReference = new WeakRef(throttled);
|
|
487
|
+
registration.throttleds.add(weakReference);
|
|
488
|
+
finalizationRegistry.register(throttled, {
|
|
489
|
+
signalWeakRef: new WeakRef(signal),
|
|
490
|
+
weakReference: weakReference
|
|
491
|
+
});
|
|
492
|
+
}
|
|
414
493
|
throttled.isEnabled = true;
|
|
415
494
|
Object.defineProperty(throttled, "queueSize", {
|
|
416
495
|
get: function get() {
|
|
417
|
-
return queue.size;
|
|
496
|
+
return state.queue.size;
|
|
418
497
|
}
|
|
419
498
|
});
|
|
499
|
+
states.set(throttled, state);
|
|
420
500
|
return throttled;
|
|
421
501
|
};
|
|
422
502
|
}
|
package/package.json
CHANGED
|
@@ -1,35 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@common.js/p-throttle",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.1.1",
|
|
4
4
|
"description": "p-throttle package exported as CommonJS modules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "etienne-martin/common.js",
|
|
7
7
|
"funding": "https://github.com/sponsors/sindresorhus",
|
|
8
8
|
"type": "commonjs",
|
|
9
|
+
"exports": {
|
|
10
|
+
"types": "./index.d.ts",
|
|
11
|
+
"default": "./index.js"
|
|
12
|
+
},
|
|
9
13
|
"sideEffects": false,
|
|
10
14
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
15
|
+
"node": ">=20"
|
|
12
16
|
},
|
|
13
17
|
"scripts": {
|
|
14
|
-
"test": "xo && ava && tsd"
|
|
18
|
+
"test": "xo && ava && tsd",
|
|
19
|
+
"test-gc": "node --expose-gc gc-test"
|
|
15
20
|
},
|
|
16
21
|
"files": [
|
|
17
22
|
"index.js",
|
|
18
23
|
"index.d.ts"
|
|
19
24
|
],
|
|
20
25
|
"devDependencies": {
|
|
21
|
-
"ava": "^
|
|
26
|
+
"ava": "^6.2.0",
|
|
22
27
|
"delay": "^6.0.0",
|
|
23
28
|
"in-range": "^3.0.0",
|
|
29
|
+
"pretty-bytes": "^7.0.1",
|
|
24
30
|
"time-span": "^5.1.0",
|
|
25
|
-
"tsd": "^0.
|
|
26
|
-
"xo": "^0.
|
|
31
|
+
"tsd": "^0.31.2",
|
|
32
|
+
"xo": "^0.59.3"
|
|
27
33
|
},
|
|
28
34
|
"ava": {
|
|
29
35
|
"serial": true
|
|
30
36
|
},
|
|
31
37
|
"homepage": "https://github.com/etienne-martin/common.js#readme",
|
|
32
38
|
"dependencies": {},
|
|
33
|
-
"
|
|
34
|
-
|
|
39
|
+
"commonjs": {
|
|
40
|
+
"source": {
|
|
41
|
+
"name": "p-throttle",
|
|
42
|
+
"version": "8.1.0"
|
|
43
|
+
},
|
|
44
|
+
"transformRevision": 1,
|
|
45
|
+
"buildKey": "dc9fd4993625694705f5a23ebe01a14ec40389d3f7a81ad39f621e0f9b38e09b"
|
|
46
|
+
},
|
|
47
|
+
"main": "./index.js",
|
|
48
|
+
"types": "./index.d.ts"
|
|
35
49
|
}
|