From ad8afe6f46429e726b32fdedf063c553ebcb0196 Mon Sep 17 00:00:00 2001 From: Chow Loong Jin Date: Wed, 1 Nov 2023 14:13:46 +0800 Subject: [PATCH 1/3] Fix AsyncResource propagation issue (#71) --- async-hooks-stub.js | 15 +++++++++++++++ index.js | 5 ++++- package.json | 9 ++++++++- test.js | 18 ++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 async-hooks-stub.js diff --git a/async-hooks-stub.js b/async-hooks-stub.js new file mode 100644 index 0000000..913c7c6 --- /dev/null +++ b/async-hooks-stub.js @@ -0,0 +1,15 @@ +export const AsyncResource = { + bind(fn, _type, thisArg) { + return fn.bind(thisArg); + }, +}; + +export class AsyncLocalStorage { + getStore() { + return undefined; + } + + run(_store, callback) { + return callback(); + } +} diff --git a/index.js b/index.js index b54b99b..c5ebcc9 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ import Queue from 'yocto-queue'; +import {AsyncResource} from '#async_hooks'; export default function pLimit(concurrency) { if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) { @@ -31,7 +32,9 @@ export default function pLimit(concurrency) { }; const enqueue = (fn, resolve, args) => { - queue.enqueue(run.bind(undefined, fn, resolve, args)); + queue.enqueue( + AsyncResource.bind(run.bind(undefined, fn, resolve, args)), + ); (async () => { // This function needs to wait until the next microtask before comparing diff --git a/package.json b/package.json index 2e41e6e..22aba98 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,12 @@ }, "type": "module", "exports": "./index.js", + "imports": { + "#async_hooks": { + "node": "async_hooks", + "default": "./async-hooks-stub.js" + } + }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -20,7 +26,8 @@ }, "files": [ "index.js", - "index.d.ts" + "index.d.ts", + "async-hooks-stub.js" ], "keywords": [ "promise", diff --git a/test.js b/test.js index fd61a97..c1bcee3 100644 --- a/test.js +++ b/test.js @@ -3,6 +3,7 @@ import delay from 'delay'; import inRange from 'in-range'; import timeSpan from 'time-span'; import randomInt from 'random-int'; +import {AsyncLocalStorage} from '#async_hooks'; import pLimit from './index.js'; test('concurrency: 1', async t => { @@ -40,6 +41,23 @@ test('concurrency: 4', async t => { await Promise.all(input); }); +test('propagates async execution context properly', async t => { + const concurrency = 2; + const limit = pLimit(concurrency); + const store = new AsyncLocalStorage(); + + const checkId = async id => { + await Promise.resolve(); + t.is(id, store.getStore()?.id); + }; + + const startContext = async id => store.run({id}, () => limit(checkId, id)); + + await Promise.all( + Array.from({length: 100}, (_, id) => startContext(id)), + ); +}); + test('non-promise returning function', async t => { await t.notThrowsAsync(async () => { const limit = pLimit(1); From 23d61ba372ebbed8c02f436cf63241727dd163cb Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 1 Nov 2023 13:17:20 +0700 Subject: [PATCH 2/3] Require Node.js 18 --- .github/workflows/main.yml | 7 ++++--- index.d.ts | 10 ++++------ index.js | 14 +++++++------- package.json | 17 ++++++++++------- readme.md | 19 ++++--------------- test.js | 2 +- 6 files changed, 30 insertions(+), 39 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 441975c..346585c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,10 +10,11 @@ jobs: fail-fast: false matrix: node-version: - - 16 + - 20 + - 18 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/index.d.ts b/index.d.ts index caae030..303c7d9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/member-ordering */ - -export interface LimitFunction { +export type LimitFunction = { /** The number of promises that are currently running. */ @@ -26,10 +24,10 @@ export interface LimitFunction { @returns The promise returned by calling `fn(...arguments)`. */ ( - fn: (...arguments: Arguments) => PromiseLike | ReturnType, - ...arguments: Arguments + fn: (...arguments_: Arguments) => PromiseLike | ReturnType, + ...arguments_: Arguments ): Promise; -} +}; /** Run multiple promise-returning & async functions with limited concurrency. diff --git a/index.js b/index.js index c5ebcc9..4a5a0f0 100644 --- a/index.js +++ b/index.js @@ -17,10 +17,10 @@ export default function pLimit(concurrency) { } }; - const run = async (fn, resolve, args) => { + const run = async (function_, resolve, arguments_) => { activeCount++; - const result = (async () => fn(...args))(); + const result = (async () => function_(...arguments_))(); resolve(result); @@ -31,9 +31,9 @@ export default function pLimit(concurrency) { next(); }; - const enqueue = (fn, resolve, args) => { + const enqueue = (function_, resolve, arguments_) => { queue.enqueue( - AsyncResource.bind(run.bind(undefined, fn, resolve, args)), + AsyncResource.bind(run.bind(undefined, function_, resolve, arguments_)), ); (async () => { @@ -49,8 +49,8 @@ export default function pLimit(concurrency) { })(); }; - const generator = (fn, ...args) => new Promise(resolve => { - enqueue(fn, resolve, args); + const generator = (function_, ...arguments_) => new Promise(resolve => { + enqueue(function_, resolve, arguments_); }); Object.defineProperties(generator, { @@ -61,7 +61,7 @@ export default function pLimit(concurrency) { get: () => queue.size, }, clearQueue: { - value: () => { + value() { queue.clear(); }, }, diff --git a/package.json b/package.json index 22aba98..606666d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "url": "https://sindresorhus.com" }, "type": "module", - "exports": "./index.js", + "exports": { + "types": "./index.d.ts", + "default": "./index.js" + }, "imports": { "#async_hooks": { "node": "async_hooks", @@ -19,7 +22,7 @@ } }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "scripts": { "test": "xo && ava && tsd" @@ -50,12 +53,12 @@ "yocto-queue": "^1.0.0" }, "devDependencies": { - "ava": "^3.15.0", - "delay": "^5.0.0", + "ava": "^5.3.1", + "delay": "^6.0.0", "in-range": "^3.0.0", "random-int": "^3.0.0", - "time-span": "^5.0.0", - "tsd": "^0.17.0", - "xo": "^0.44.0" + "time-span": "^5.1.0", + "tsd": "^0.29.0", + "xo": "^0.56.0" } } diff --git a/readme.md b/readme.md index b02253b..4e890f8 100644 --- a/readme.md +++ b/readme.md @@ -2,10 +2,12 @@ > Run multiple promise-returning & async functions with limited concurrency +*Works in Node.js and browsers.* + ## Install -``` -$ npm install p-limit +```sh +npm install p-limit ``` ## Usage @@ -80,20 +82,7 @@ This package is only about limiting the number of concurrent executions, while ` ## Related -- [p-queue](https://github.com/sindresorhus/p-queue) - Promise queue with concurrency control - [p-throttle](https://github.com/sindresorhus/p-throttle) - Throttle promise-returning & async functions - [p-debounce](https://github.com/sindresorhus/p-debounce) - Debounce promise-returning & async functions - [p-all](https://github.com/sindresorhus/p-all) - Run promise-returning & async functions concurrently with optional limited concurrency - [More…](https://github.com/sindresorhus/promise-fun) - ---- - -
- - Get professional support for this package with a Tidelift subscription - -
- - Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. -
-
diff --git a/test.js b/test.js index c1bcee3..6477920 100644 --- a/test.js +++ b/test.js @@ -3,8 +3,8 @@ import delay from 'delay'; import inRange from 'in-range'; import timeSpan from 'time-span'; import randomInt from 'random-int'; -import {AsyncLocalStorage} from '#async_hooks'; import pLimit from './index.js'; +import {AsyncLocalStorage} from '#async_hooks'; test('concurrency: 1', async t => { const input = [ From f53bdb5f464ae112b2859e834fdebedc0745199b Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 1 Nov 2023 13:29:14 +0700 Subject: [PATCH 3/3] 5.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 606666d..6d5a9b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "p-limit", - "version": "4.0.0", + "version": "5.0.0", "description": "Run multiple promise-returning & async functions with limited concurrency", "license": "MIT", "repository": "sindresorhus/p-limit",