前言
作为一名前端面试官,我经常遇到这样的候选人:聊 Promise API 的时候说的很溜,我感觉我都没他这么溜,然后再问实际的 Promise 业务场景的时候就不会说了。今天我想分享一个常见的面试题:如何实现并发控制?
面试现场
"你对Promise熟悉吗?" "当然!Promise.all、Promise.race、Promise.any..." "好的,那我们来看一个实际场景..."
场景:批量上传文件
"假设用户要上传100张图片,但是服务器限制同时最多只能处理5个请求,你会怎么处理?"
很多候选人开始慌了:
- "用Promise.all?"
- "for循环发请求?"
- "递归调用?"
问题的本质
其实这类问题的核心是:并发控制。
不是考察对Promise API的记忆,而是考察:
- 对异步任务的理解
- 对并发控制的认知
- 对实际业务场景的处理能力
怎么解决
让我们实现一个通用的并发控制队列:
ts
export class TaskQueue {
private queue: (() => Promise<any>)[] = [];
private activeCount = 0;
private maxConcurrent: number;
constructor(maxConcurrent: number) {
this.maxConcurrent = maxConcurrent;
}
public get pending(): number {
return this.queue.length;
}
public get active(): number {
return this.activeCount;
}
public clear(): void {
this.queue = [];
}
private next() {
if (this.queue.length === 0 || this.activeCount >= this.maxConcurrent) {
return;
}
const task = this.queue.shift();
if (task) {
this.activeCount++;
task().finally(() => {
this.activeCount--;
this.next();
});
}
}
public add<T>(fn: () => Promise<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
const task = async () => {
try {
resolve(await fn());
} catch (error) {
reject(error);
}
};
this.queue.push(task);
this.next();
});
}
}
代码解析
- 类的属性
- queue : 存储待执行的任务队列
- activeCount : 当前正在执行的任务数量
- maxConcurrent : 最大并发数
- 核心方法
- add : 添加新任务到队列
- next : 执行下一个任务
- clear : 清空任务队列
- pending : 获取待执行任务数量
- active : 获取当前执行中的任务数量
示例
ts
// 创建队列实例,最大并发数为2
const queue = new TaskQueue(2);
// 模拟异步任务
const createTask = (id: number) => {
return () => new Promise<string>((resolve) => {
const duration = Math.random() * 2000;
setTimeout(() => {
console.log(`Task ${id} completed`);
resolve(`Task ${id} result`);
}, duration);
});
};
// 添加任务
async function runTasks() {
console.log('开始执行任务');
// 添加5个任务
for (let i = 1; i <= 5; i++) {
queue.add(createTask(i))
.then(result => console.log(result));
console.log(`Task ${i} added, pending: ${queue.pending}, active: ${queue.active}`);
}
}
runTasks();
总结
通过这个例子,我们可以看到: 知道API的使用,并不意味着你就会用。但更重要的是理解它能解决什么实际问题
希望这篇文章对你有帮助!如果你有任何问题或建议,欢迎在评论区讨论。