一个前端面试官的思考:当Promise遇上真实业务场景

前言

作为一名前端面试官,我经常遇到这样的候选人:聊 Promise API 的时候说的很溜,我感觉我都没他这么溜,然后再问实际的 Promise 业务场景的时候就不会说了。今天我想分享一个常见的面试题:如何实现并发控制?

面试现场

"你对Promise熟悉吗?" "当然!Promise.all、Promise.race、Promise.any..." "好的,那我们来看一个实际场景..."

场景:批量上传文件

"假设用户要上传100张图片,但是服务器限制同时最多只能处理5个请求,你会怎么处理?"

很多候选人开始慌了:

  • "用Promise.all?"
  • "for循环发请求?"
  • "递归调用?"

问题的本质

其实这类问题的核心是:并发控制。

不是考察对Promise API的记忆,而是考察:

  1. 对异步任务的理解
  2. 对并发控制的认知
  3. 对实际业务场景的处理能力

怎么解决

让我们实现一个通用的并发控制队列:

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的使用,并不意味着你就会用。但更重要的是理解它能解决什么实际问题

希望这篇文章对你有帮助!如果你有任何问题或建议,欢迎在评论区讨论。

相关推荐
努力学习的小刘1 分钟前
如何使用react-router实现动态路由
前端·javascript
PasserbyX1 分钟前
JS原型链
前端·javascript
curdcv_po1 分钟前
你知道Cookie的弊端吗?
前端
curdcv_po3 分钟前
前端CSS高频面试题详解
前端
汪子熙5 分钟前
深入解析互斥锁(Mutex):并发编程中的关键同步机制
后端·面试
Danta7 分钟前
从0开始学习three.js(1)😁
前端·javascript·three.js
我的心巴7 分钟前
vue-print-nb 打印相关问题
前端·vue.js·elementui
coderYYY27 分钟前
element树结构el-tree,默认选中当前setCurrentKey无效
前端·javascript·vue.js
GISer_Jing1 小时前
[总结篇]个人网站
前端·javascript
疯狂的沙粒1 小时前
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
前端·uni-app·html