plimit-限制并发

84 阅读1分钟
import Queue from 'yocto-queue';
2
3export default function pLimit(concurrency) {
4	validateConcurrency(concurrency);
5
6	const queue = new Queue();
7	let activeCount = 0;
8
9	const resumeNext = () => {
10		if (activeCount < concurrency && queue.size > 0) {
11			queue.dequeue()();
12			// Since `pendingCount` has been decreased by one, increase `activeCount` by one.
13			activeCount++;
14		}
15	};
16
17	const next = () => {
18		activeCount--;
19
20		resumeNext();
21	};
22
23	const run = async (function_, resolve, arguments_) => {
24		const result = (async () => function_(...arguments_))();
25
26		resolve(result);
27
28		try {
29			await result;
30		} catch {}
31
32		next();
33	};
34
35	const enqueue = (function_, resolve, arguments_) => {
36		// Queue `internalResolve` instead of the `run` function
37		// to preserve asynchronous context.
38		new Promise(internalResolve => {
39			queue.enqueue(internalResolve);
40		}).then(
41			run.bind(undefined, function_, resolve, arguments_),
42		);
43
44		(async () => {
45			// This function needs to wait until the next microtask before comparing
46			// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
47			// after the `internalResolve` function is dequeued and called. The comparison in the if-statement
48			// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
49			await Promise.resolve();
50
51			if (activeCount < concurrency) {
52				resumeNext();
53			}
54		})();
55	};
56
57	const generator = (function_, ...arguments_) => new Promise(resolve => {
58		enqueue(function_, resolve, arguments_);
59	});
60
61	Object.defineProperties(generator, {
62		activeCount: {
63			get: () => activeCount,
64		},
65		pendingCount: {
66			get: () => queue.size,
67		},
68		clearQueue: {
69			value() {
70				queue.clear();
71			},
72		},
73		concurrency: {
74			get: () => concurrency,
75
76			set(newConcurrency) {
77				validateConcurrency(newConcurrency);
78				concurrency = newConcurrency;
79
80				queueMicrotask(() => {
81					// eslint-disable-next-line no-unmodified-loop-condition
82					while (activeCount < concurrency && queue.size > 0) {
83						resumeNext();
84					}
85				});
86			},
87		},
88	});
89
90	return generator;
91}
92
93function validateConcurrency(concurrency) {
94	if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
95		throw new TypeError('Expected `concurrency` to be a number from 1 and up');
96	}
97}
import pLimit from 'p-limit';

const limit = pLimit(1);

const input = [
	limit(() => fetchSomething('foo')),
	limit(() => fetchSomething('bar')),
	limit(() => doSomething())
];

// Only one promise is run at once
const result = await Promise.all(input);
console.log(result);