一个前端面试官的思考:当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的使用,并不意味着你就会用。但更重要的是理解它能解决什么实际问题

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

相关推荐
05091517 分钟前
测试基础笔记第四天(html)
前端·笔记·html
聪明的墨菲特i1 小时前
React与Vue:哪个框架更适合入门?
开发语言·前端·javascript·vue.js·react.js
时光少年1 小时前
Android 副屏录制方案
android·前端
拉不动的猪1 小时前
v2升级v3需要兼顾的几个方面
前端·javascript·面试
时光少年1 小时前
Android 局域网NIO案例实践
android·前端
_一条咸鱼_1 小时前
AI 大模型的 Prompt Engineering 原理
人工智能·深度学习·面试
半兽先生1 小时前
VueDOMPurifyHTML 防止 XSS(跨站脚本攻击) 风险
前端·xss
冴羽1 小时前
SvelteKit 最新中文文档教程(20)—— 最佳实践之性能
前端·javascript·svelte
懒懒小徐1 小时前
消息中间件面试题
java·开发语言·面试·消息队列
Jackson__1 小时前
面试官:谈一下在 ts 中你对 any 和 unknow 的理解
前端·typescript