@common.js/p-limit 5.0.0 → 7.3.0

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 CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  The [p-limit](https://www.npmjs.com/package/p-limit) package exported as CommonJS modules.
4
4
 
5
- Exported from [p-limit@5.0.0](https://www.npmjs.com/package/p-limit/v/5.0.0) using https://github.com/etienne-martin/common.js.
5
+ Exported from [p-limit@7.3.0](https://www.npmjs.com/package/p-limit/v/7.3.0) using https://github.com/etienne-martin/common.js.
package/index.d.ts CHANGED
@@ -9,22 +9,49 @@ export type LimitFunction = {
9
9
  */
10
10
  readonly pendingCount: number;
11
11
 
12
+ /**
13
+ Get or set the concurrency limit.
14
+ */
15
+ concurrency: number;
16
+
12
17
  /**
13
18
  Discard pending promises that are waiting to run.
14
19
 
15
20
  This might be useful if you want to teardown the queue at the end of your program's lifecycle or discard any function calls referencing an intermediary state of your app.
16
21
 
17
22
  Note: This does not cancel promises that are already running.
23
+
24
+ When `rejectOnClear` is enabled, pending promises are rejected with an `AbortError`.
25
+ This is recommended if you await the returned promises, for example with `Promise.all`, so pending tasks do not remain unresolved after `clearQueue()`.
18
26
  */
19
27
  clearQueue: () => void;
20
28
 
29
+ /**
30
+ Process an iterable of inputs with limited concurrency.
31
+
32
+ The mapper function receives the item value and its index.
33
+
34
+ This is a convenience function for processing inputs that arrive in batches. For more complex use cases, see [p-map](https://github.com/sindresorhus/p-map).
35
+
36
+ @param iterable - An iterable containing an argument for the given function.
37
+ @param mapperFunction - Promise-returning/async function.
38
+ @returns A promise equivalent to `Promise.all(Array.from(iterable, (item, index) => limit(mapperFunction, item, index)))`.
39
+ */
40
+ map: <Input, ReturnType> (
41
+ iterable: Iterable<Input>,
42
+ mapperFunction: (input: Input, index: number) => PromiseLike<ReturnType> | ReturnType
43
+ ) => Promise<ReturnType[]>;
44
+
21
45
  /**
22
46
  @param fn - Promise-returning/async function.
23
- @param arguments - Any arguments to pass through to `fn`. Support for passing arguments on to the `fn` is provided in order to be able to avoid creating unnecessary closures. You probably don't need this optimization unless you're pushing a lot of functions.
47
+ @param arguments - Any arguments to pass through to `fn`. Support for passing arguments on to the `fn` is provided in order to be able to avoid creating unnecessary closures. You probably don't need this optimization unless you're pushing a *lot* of functions.
48
+
49
+ Warning: Avoid calling the same `limit` function inside a function that is already limited by it. This can create a deadlock where inner tasks never run. Use a separate limiter for inner tasks.
50
+
24
51
  @returns The promise returned by calling `fn(...arguments)`.
25
52
  */
26
53
  <Arguments extends unknown[], ReturnType>(
27
- fn: (...arguments_: Arguments) => PromiseLike<ReturnType> | ReturnType,
54
+ function_: (...arguments_: Arguments) => PromiseLike<ReturnType> | ReturnType,
28
55
  ...arguments_: Arguments
29
56
  ): Promise<ReturnType>;
30
57
  };
@@ -32,7 +59,78 @@ export type LimitFunction = {
32
59
  /**
33
60
  Run multiple promise-returning & async functions with limited concurrency.
34
61
 
35
- @param concurrency - Concurrency limit. Minimum: `1`.
62
+ @param concurrency - Concurrency limit. Minimum: `1`. You can pass a number or an options object with a `concurrency` property.
36
63
  @returns A `limit` function.
64
+
65
+ @example
66
+ ```
67
+ import pLimit from 'p-limit';
68
+
69
+ const limit = pLimit(1);
70
+
71
+ const input = [
72
+ limit(() => fetchSomething('foo')),
73
+ limit(() => fetchSomething('bar')),
74
+ limit(() => doSomething())
75
+ ];
76
+
77
+ // Only one promise is run at once
78
+ const result = await Promise.all(input);
79
+ console.log(result);
80
+ ```
81
+
82
+ @example
83
+ ```
84
+ import pLimit from 'p-limit';
85
+
86
+ const limit = pLimit({concurrency: 1});
87
+ ```
88
+ */
89
+ export default function pLimit(concurrency: number | Options): LimitFunction;
90
+
91
+ export type Options = {
92
+ /**
93
+ Concurrency limit.
94
+
95
+ Minimum: `1`.
96
+ */
97
+ readonly concurrency: number;
98
+
99
+ /**
100
+ Reject pending promises with an `AbortError` when `clearQueue()` is called.
101
+
102
+ Default: `false`.
103
+
104
+ This is recommended if you await the returned promises, for example with `Promise.all`, so pending tasks do not remain unresolved after `clearQueue()`.
105
+ */
106
+ readonly rejectOnClear?: boolean;
107
+ };
108
+
109
+ /**
110
+ Returns a function with limited concurrency.
111
+
112
+ The returned function manages its own concurrent executions, allowing you to call it multiple times without exceeding the specified concurrency limit.
113
+
114
+ Ideal for scenarios where you need to control the number of simultaneous executions of a single function, rather than managing concurrency across multiple functions.
115
+
116
+ @param function_ - Promise-returning/async function.
117
+ @return Function with limited concurrency.
118
+
119
+ @example
120
+ ```
121
+ import {limitFunction} from 'p-limit';
122
+
123
+ const limitedFunction = limitFunction(async () => {
124
+ return doSomething();
125
+ }, {concurrency: 1});
126
+
127
+ const input = Array.from({length: 10}, limitedFunction);
128
+
129
+ // Only one promise is run at once.
130
+ await Promise.all(input);
131
+ ```
37
132
  */
38
- export default function pLimit(concurrency: number): LimitFunction;
133
+ export function limitFunction<Arguments extends unknown[], ReturnType>(
134
+ function_: (...arguments_: Arguments) => PromiseLike<ReturnType>,
135
+ options: Options
136
+ ): (...arguments_: Arguments) => Promise<ReturnType>;
package/index.js CHANGED
@@ -2,14 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", {
3
3
  value: true
4
4
  });
5
- Object.defineProperty(exports, "default", {
6
- enumerable: true,
7
- get: function() {
5
+ function _export(target, all) {
6
+ for(var name in all)Object.defineProperty(target, name, {
7
+ enumerable: true,
8
+ get: all[name]
9
+ });
10
+ }
11
+ _export(exports, {
12
+ default: function() {
8
13
  return pLimit;
14
+ },
15
+ limitFunction: function() {
16
+ return limitFunction;
9
17
  }
10
18
  });
11
19
  var _yoctoQueue = /*#__PURE__*/ _interopRequireDefault(require("yocto-queue"));
12
- var _asyncHooks = require("#async_hooks");
13
20
  function _arrayLikeToArray(arr, len) {
14
21
  if (len == null || len > arr.length) len = arr.length;
15
22
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
@@ -165,16 +172,27 @@ var __generator = (void 0) && (void 0).__generator || function(thisArg, body) {
165
172
  }
166
173
  };
167
174
  function pLimit(concurrency) {
168
- if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
169
- throw new TypeError("Expected `concurrency` to be a number from 1 and up");
175
+ var rejectOnClear = false;
176
+ if (typeof concurrency === "object") {
177
+ var ref, ref1;
178
+ ref = concurrency, concurrency = ref.concurrency, ref1 = ref.rejectOnClear, rejectOnClear = ref1 === void 0 ? false : ref1, ref;
179
+ }
180
+ validateConcurrency(concurrency);
181
+ if (typeof rejectOnClear !== "boolean") {
182
+ throw new TypeError("Expected `rejectOnClear` to be a boolean");
170
183
  }
171
184
  var queue = new _yoctoQueue.default();
172
185
  var activeCount = 0;
186
+ var resumeNext = function() {
187
+ // Process the next queued function if we're under the concurrency limit
188
+ if (activeCount < concurrency && queue.size > 0) {
189
+ activeCount++;
190
+ queue.dequeue().run();
191
+ }
192
+ };
173
193
  var next = function() {
174
194
  activeCount--;
175
- if (queue.size > 0) {
176
- queue.dequeue()();
177
- }
195
+ resumeNext();
178
196
  };
179
197
  var run = function() {
180
198
  var _ref = _asyncToGenerator(function(function_, resolve, arguments_) {
@@ -182,7 +200,6 @@ function pLimit(concurrency) {
182
200
  return __generator(this, function(_state) {
183
201
  switch(_state.label){
184
202
  case 0:
185
- activeCount++;
186
203
  result = _asyncToGenerator(function() {
187
204
  return __generator(this, function(_state) {
188
205
  return [
@@ -191,6 +208,7 @@ function pLimit(concurrency) {
191
208
  ];
192
209
  });
193
210
  })();
211
+ // Resolve immediately with the promise (don't wait for completion)
194
212
  resolve(result);
195
213
  _state.label = 1;
196
214
  case 1:
@@ -217,6 +235,7 @@ function pLimit(concurrency) {
217
235
  4
218
236
  ];
219
237
  case 4:
238
+ // Decrement active count and process next queued function
220
239
  next();
221
240
  return [
222
241
  2
@@ -228,38 +247,27 @@ function pLimit(concurrency) {
228
247
  return _ref.apply(this, arguments);
229
248
  };
230
249
  }();
231
- var enqueue = function(function_, resolve, arguments_) {
232
- queue.enqueue(_asyncHooks.AsyncResource.bind(run.bind(undefined, function_, resolve, arguments_)));
233
- _asyncToGenerator(function() {
234
- return __generator(this, function(_state) {
235
- switch(_state.label){
236
- case 0:
237
- // This function needs to wait until the next microtask before comparing
238
- // `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
239
- // when the run function is dequeued and called. The comparison in the if-statement
240
- // needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
241
- return [
242
- 4,
243
- Promise.resolve()
244
- ];
245
- case 1:
246
- _state.sent();
247
- if (activeCount < concurrency && queue.size > 0) {
248
- queue.dequeue()();
249
- }
250
- return [
251
- 2
252
- ];
253
- }
254
- });
255
- })();
250
+ var enqueue = function(function_, resolve, reject, arguments_) {
251
+ var queueItem = {
252
+ reject: reject
253
+ };
254
+ // Queue the internal resolve function instead of the run function
255
+ // to preserve the asynchronous execution context.
256
+ new Promise(function(internalResolve) {
257
+ queueItem.run = internalResolve;
258
+ queue.enqueue(queueItem);
259
+ }).then(run.bind(undefined, function_, resolve, arguments_)); // eslint-disable-line promise/prefer-await-to-then
260
+ // Start processing immediately if we haven't reached the concurrency limit
261
+ if (activeCount < concurrency) {
262
+ resumeNext();
263
+ }
256
264
  };
257
265
  var generator = function(function_) {
258
266
  for(var _len = arguments.length, arguments_ = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++){
259
267
  arguments_[_key - 1] = arguments[_key];
260
268
  }
261
- return new Promise(function(resolve) {
262
- enqueue(function_, resolve, arguments_);
269
+ return new Promise(function(resolve, reject) {
270
+ enqueue(function_, resolve, reject, arguments_);
263
271
  });
264
272
  };
265
273
  Object.defineProperties(generator, {
@@ -275,9 +283,64 @@ function pLimit(concurrency) {
275
283
  },
276
284
  clearQueue: {
277
285
  value: function value() {
278
- queue.clear();
286
+ if (!rejectOnClear) {
287
+ queue.clear();
288
+ return;
289
+ }
290
+ var abortError = AbortSignal.abort().reason;
291
+ while(queue.size > 0){
292
+ queue.dequeue().reject(abortError);
293
+ }
294
+ }
295
+ },
296
+ concurrency: {
297
+ get: function() {
298
+ return concurrency;
299
+ },
300
+ set: function set(newConcurrency) {
301
+ validateConcurrency(newConcurrency);
302
+ concurrency = newConcurrency;
303
+ queueMicrotask(function() {
304
+ // eslint-disable-next-line no-unmodified-loop-condition
305
+ while(activeCount < concurrency && queue.size > 0){
306
+ resumeNext();
307
+ }
308
+ });
309
+ }
310
+ },
311
+ map: {
312
+ value: function value(iterable, function_) {
313
+ return _asyncToGenerator(function() {
314
+ var _this, promises;
315
+ return __generator(this, function(_state) {
316
+ _this = this;
317
+ promises = Array.from(iterable, function(value, index) {
318
+ return _this(function_, value, index);
319
+ });
320
+ return [
321
+ 2,
322
+ Promise.all(promises)
323
+ ];
324
+ });
325
+ }).apply(this);
279
326
  }
280
327
  }
281
328
  });
282
329
  return generator;
283
330
  }
331
+ function limitFunction(function_, options) {
332
+ var limit = pLimit(options);
333
+ return function() {
334
+ for(var _len = arguments.length, arguments_ = new Array(_len), _key = 0; _key < _len; _key++){
335
+ arguments_[_key] = arguments[_key];
336
+ }
337
+ return limit(function() {
338
+ return function_.apply(void 0, _toConsumableArray(arguments_));
339
+ });
340
+ };
341
+ }
342
+ function validateConcurrency(concurrency) {
343
+ if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
344
+ throw new TypeError("Expected `concurrency` to be a number from 1 and up");
345
+ }
346
+ }
package/package.json CHANGED
@@ -1,41 +1,35 @@
1
1
  {
2
2
  "name": "@common.js/p-limit",
3
- "version": "5.0.0",
3
+ "version": "7.3.0",
4
4
  "description": "p-limit 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
- "imports": {
10
- "#async_hooks": {
11
- "node": "async_hooks",
12
- "default": "./async-hooks-stub.js"
13
- }
14
- },
9
+ "sideEffects": false,
15
10
  "engines": {
16
- "node": ">=18"
11
+ "node": ">=20"
17
12
  },
18
13
  "scripts": {
19
- "test": "xo && ava && tsd"
14
+ "test": "xo && ava && tsd",
15
+ "benchmark": "node benchmark.js"
20
16
  },
21
17
  "files": [
22
18
  "index.js",
23
- "index.d.ts",
24
- "async-hooks-stub.js"
19
+ "index.d.ts"
25
20
  ],
26
21
  "dependencies": {
27
- "@common.js/yocto-queue": "^1.0.0"
22
+ "@common.js/yocto-queue": "^1.2.1"
28
23
  },
29
24
  "devDependencies": {
30
- "ava": "^5.3.1",
31
- "delay": "^6.0.0",
25
+ "ava": "^6.4.1",
32
26
  "in-range": "^3.0.0",
33
27
  "random-int": "^3.0.0",
34
28
  "time-span": "^5.1.0",
35
- "tsd": "^0.29.0",
36
- "xo": "^0.56.0"
29
+ "tsd": "^0.33.0",
30
+ "xo": "^1.2.1"
37
31
  },
38
32
  "homepage": "https://github.com/etienne-martin/common.js#readme",
39
- "types": "./index.d.ts",
40
- "main": "./index.js"
33
+ "main": "./index.js",
34
+ "types": "./index.d.ts"
41
35
  }
@@ -1,63 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: all[name]
9
- });
10
- }
11
- _export(exports, {
12
- AsyncResource: function() {
13
- return AsyncResource;
14
- },
15
- AsyncLocalStorage: function() {
16
- return AsyncLocalStorage;
17
- }
18
- });
19
- function _classCallCheck(instance, Constructor) {
20
- if (!(instance instanceof Constructor)) {
21
- throw new TypeError("Cannot call a class as a function");
22
- }
23
- }
24
- function _defineProperties(target, props) {
25
- for(var i = 0; i < props.length; i++){
26
- var descriptor = props[i];
27
- descriptor.enumerable = descriptor.enumerable || false;
28
- descriptor.configurable = true;
29
- if ("value" in descriptor) descriptor.writable = true;
30
- Object.defineProperty(target, descriptor.key, descriptor);
31
- }
32
- }
33
- function _createClass(Constructor, protoProps, staticProps) {
34
- if (protoProps) _defineProperties(Constructor.prototype, protoProps);
35
- if (staticProps) _defineProperties(Constructor, staticProps);
36
- return Constructor;
37
- }
38
- var AsyncResource = {
39
- bind: function bind(fn, _type, thisArg) {
40
- return fn.bind(thisArg);
41
- }
42
- };
43
- var AsyncLocalStorage = /*#__PURE__*/ function() {
44
- "use strict";
45
- function AsyncLocalStorage() {
46
- _classCallCheck(this, AsyncLocalStorage);
47
- }
48
- _createClass(AsyncLocalStorage, [
49
- {
50
- key: "getStore",
51
- value: function getStore() {
52
- return undefined;
53
- }
54
- },
55
- {
56
- key: "run",
57
- value: function run(_store, callback) {
58
- return callback();
59
- }
60
- }
61
- ]);
62
- return AsyncLocalStorage;
63
- }();