Update:
有一个模块可以做到这一点:https://www.npmjs.com/package/promise-queue https://www.npmjs.com/package/promise-queue
旧答案:
我在尝试使用各种模块时遇到了问题,最后我编写了我能想到的此类工作的最简单的实现。
看看我写的这个简单的类(Plain JS):
class Queue {
constructor(maxSimultaneously = 1) {
this.maxSimultaneously = maxSimultaneously;
this.__active = 0;
this.__queue = [];
}
/** @param { () => Promise<T> } func
* @template T
* @returns {Promise<T>}
*/
async enqueue(func) {
if(++this.__active > this.maxSimultaneously) {
await new Promise(resolve => this.__queue.push(resolve));
}
try {
return await func();
} catch(err) {
throw err;
} finally {
this.__active--;
if(this.__queue.length) {
this.__queue.shift()();
}
}
}
}
像这样使用它:
假设您有这个异步函数:
const printNumber = async (n) => {
await new Promise(res => setTimeout(res, 2000)); // wait 2 sec
console.log(n);
}
所以,而不是:
await printNumber(1);
await printNumber(2);
await printNumber(3);
await printNumber(4);
use:
const q = new Queue();
q.enqueue(() => printNumber(1));
q.enqueue(() => printNumber(2));
q.enqueue(() => printNumber(3));
q.enqueue(() => printNumber(4));
每个函数将在其他函数完成后执行。
输出:
1 // after 2 sec
2 // after 4 sec
3 // after 6 sec
4 // after 8 sec
或者您可以限制队列同时运行一定数量的函数:
const q = new Queue(3);
q.enqueue(() => printNumber(1));
q.enqueue(() => printNumber(2));
q.enqueue(() => printNumber(3));
q.enqueue(() => printNumber(4));
输出:
1 // after 2 sec
2 // after 2 sec
3 // after 2 sec
4 // after 4 sec
Also, enqueue 方法将返回/抛出您的 Promise 中的原始数据!
假设您编写了一个 API 来上传文件,并且希望将同时上传的数量限制为最多 5 个。您希望一切都保持原样,而不改变您的流程。您可以按照以下方法执行此操作:
async function upload(data) {
// upload...
if(something) {
return 200;
} else {
throw 400;
}
}
因此,不要这样做:
async function work(data) {
// do something...
return await upload(data);
}
do this:
const q = new Queue(5); // up to 5 at the same time
async function work(data) {
// do something...
return await q.enqueue(() => upload(data));
}
class Queue {
constructor(maxSimultaneously = 1) {
this.maxSimultaneously = maxSimultaneously;
this.__active = 0;
this.__queue = [];
}
/** @param { () => Promise<T> } func
* @template T
* @returns {Promise<T>}
*/
async enqueue(func) {
if(++this.__active > this.maxSimultaneously) {
await new Promise(resolve => this.__queue.push(resolve));
}
try {
return await func();
} catch(err) {
throw err;
} finally {
this.__active--;
if(this.__queue.length) {
this.__queue.shift()();
}
}
}
}
const printNumber = async (n) => {
await new Promise(res => setTimeout(res, 2000)); // wait 2 sec
console.log(n);
}
async function start() {
console.log('starting...');
const q = new Queue();
q.enqueue(() => printNumber(1));
q.enqueue(() => printNumber(2));
q.enqueue(() => printNumber(3));
q.enqueue(() => printNumber(4));
}
Click this to run 1 log per 2 sec: <button onclick="start();">Start</button>
class Queue {
constructor(maxSimultaneously = 1) {
this.maxSimultaneously = maxSimultaneously;
this.__active = 0;
this.__queue = [];
}
/** @param { () => Promise<T> } func
* @template T
* @returns {Promise<T>}
*/
async enqueue(func) {
if(++this.__active > this.maxSimultaneously) {
await new Promise(resolve => this.__queue.push(resolve));
}
try {
return await func();
} catch(err) {
throw err;
} finally {
this.__active--;
if(this.__queue.length) {
this.__queue.shift()();
}
}
}
}
const printNumber = async (n) => {
await new Promise(res => setTimeout(res, 2000)); // wait 2 sec
console.log(n);
}
async function start() {
console.log('starting...');
const q = new Queue(3);
q.enqueue(() => printNumber(1));
q.enqueue(() => printNumber(2));
q.enqueue(() => printNumber(3));
q.enqueue(() => printNumber(4));
}
Click this to run up to 3 logs every 2 sec: <button onclick="start();">Start</button>