js 如何给一个class 函数添加触发事件,外部可以监听到呢?
我想在LimitRequest中去限制并发请求数量,在外部想要知道请求是否都完成了,就要监听到currentSum的大小。如何实现呢?
export class LimitRequest {
private limit: number = 1; // 限制并发数量
private currentSum: number = 0; // 当前发送数量
private requests: Array<any> = []; // 请求
constructor(limit: number) {
this.limit = limit;
this.currentSum = 0;
this.requests = [];
}
public request(reqFn: Function) {
this.requests.push(reqFn);
if (this.currentSum < this.limit) {
this.run();
}
}
public stop() {
this.requests = [];
this.currentSum = 0;
}
async run() {
try {
++this.currentSum;
const fn = this.requests.shift();
console.log('开始开始', this.currentSum, this.requests.length);
await fn();
} catch (err) {
console.log('Error', err);
} finally {
--this.currentSum;
if (this.requests.length > 0) {
this.run();
}
}
}
}
回复
1个回答

test
2024-07-11
根据题目描述,需要 "知道请求是否都完成了",此时可以根据 requests.length
是否为 0
来判断,即如果请求列表为空时代表所有请求均已被执行。
LimitRequest.ts
export default class LimitRequest {
private limit: number = 1; // 限制并发数量
private currentSum: number = 0; // 当前发送数量
private requests: Array<any> = []; // 请求
private finishedFn: Function = () => {}; // 请求完成后的回调函数
constructor(limit: number, finishedFn = () => {}) {
this.limit = limit;
this.currentSum = 0;
this.requests = [];
// 设置 finishedFn
this.finishedFn = finishedFn;
}
public request(reqFn: Function) {
this.requests.push(reqFn);
if (this.currentSum < this.limit) {
this.run();
}
}
public stop() {
this.requests = [];
this.currentSum = 0;
}
async run() {
try {
++this.currentSum;
const fn = this.requests.shift();
console.log("开始", this.currentSum, this.requests.length);
await fn();
} catch (err) {
console.log("Error", err);
} finally {
--this.currentSum;
if (this.requests.length > 0) {
this.run();
} else {
// 如果请求列表为空,执行 finishedFn
this.finishedFn();
}
}
}
}
用法:
import LimitRequest from "./LimitRequest";
const instance = new LimitRequest(2, () => {
console.log("Finished");
});
const fn1 = function () {
console.log("fn1");
};
const fn2 = async function () {
console.log("fn2");
return "fn2";
};
const fn3 = function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("fn3");
resolve("fn3");
}, 5000);
});
};
const fn4 = function () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("fn4");
reject("fn4");
}, 5000);
});
};
const fn5 = function () {
console.log("fn5");
return function () {
return "fn5";
};
};
const fn6 = function () {
console.log("fn6");
return "fn6";
};
instance.request(fn1);
instance.request(fn2);
instance.request(fn3);
instance.request(fn4);
instance.request(fn5);
instance.request(fn6);
输出如下:
开始 1 0
fn1
开始 2 0
fn2
开始 2 3
开始 2 2
fn3
开始 2 1
fn5
开始 2 0
fn6
Finished
fn4
Error fn4
Finished
上面的实现有个小问题,Finished
打印了两次,即结束回调函数执行了两次。这是由于我们仅仅判断了 requests.length
是否为空,而没有判断传入的函数是否均已执行完成,异步函数的执行会有延迟。要解决这个问题,可以在
LimitRequest
这个类中为每一个传入的请求函数增加一个唯一的 id,用于识别每个函数。- 新增一个属性
pendingRequests
记录执行中的函数的 id,在请求函数执行完毕后再从pendingRequests
中删除其 id。 - 最后函数执行完毕后判断
pendingRequests
和requests
均为空,即可以得知所有请求执行完成。
实现:
export default class LimitRequest {
private limit: number = 1; // 限制并发数量
private currentSum: number = 0; // 当前发送数量
private requests: Array<any> = []; // 请求
private finishedFn: Function = () => {}; // 请求完成后的回调函数
private pendingRequests = new Set(); // Set 存储正在执行中的请求的 id
private fnId = 0; // 自增的 id,用于唯一识别传入的函数
constructor(limit: number, finishedFn = () => {}) {
this.limit = limit;
this.currentSum = 0;
this.requests = [];
// 设置 finishedFn
this.finishedFn = finishedFn;
}
public request(reqFn: Function) {
const fnId = this.fnId;
// @ts-ignore
reqFn.id = fnId; // 为请求函数分配一个 id
this.requests.push(reqFn);
this.pendingRequests.add(fnId); // 把此 id 加入 Set
this.fnId++; // id 自增
if (this.currentSum < this.limit) {
this.run();
}
}
public stop() {
this.requests = [];
this.currentSum = 0;
this.pendingRequests.clear(); // 清空执行中函数的 id 列表
}
async run() {
let fn: any;
//console.log("this.pendingRequests:", this.pendingRequests);
try {
++this.currentSum;
fn = this.requests.shift();
console.log("开始", this.currentSum, this.requests.length);
await fn();
} catch (err) {
console.log("Error", err);
} finally {
// 当前函数执行完毕后,从 Set 中删除其 id
this.pendingRequests.delete(fn.id);
--this.currentSum;
if (this.requests.length > 0) {
this.run();
} else {
// 如果 Set 为空,说明所有请求已执行完成
if (this.pendingRequests.size === 0) {
// 如果请求列表为空,执行 finishedFn
this.finishedFn();
}
}
}
}
}
输出:
开始 1 0
fn1
开始 2 0
fn2
开始 2 3
开始 2 2
fn3
开始 2 1
fn5
开始 2 0
fn6
fn4
Error fn4
Finished
回复

适合作为回答的
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容