Linca

Linca

@linca

偶然在小红书上刷到一个前端面试题,顺手做了一下。

要求:实现一个带并发限制的异步调度器 Scheduler,保证同时运行的异步任务最多 MAX_LENGTH 个,使得以下程序能正确输出

const timeout = (time) =>
  new Promise((resolve) => {
    setTimeout(resolve, time);
  });
const MAX_LENGTH = 2;
const scheduler = new Scheduler(MAX_LENGTH);
const addTask = (time, order) => {
  scheduler
    .add(() => {
      return timeout(time);
    })
    .then(() => console.log(order));
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");

//  output: 2 3 1 4
// 一开始,1、2两个任务进入队列
// 500ms时,2完成,输出2,任务3进队
// 800ms时,3完成,输出3,任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4

感觉还是有点简单,五分钟内肯定是能做出来了。

题解

一开始漏了异常处理的情况,让 gpt 看了眼指了出来

class Scheduler {
  running = 0;
  pending_tasks: (() => void)[] = [];
  queue_len: number;

  constructor(queue_len: number) {
    this.queue_len = queue_len;
  }

  async add<T>(promiseCb: () => Promise<T>): Promise<T> {
    if (this.running >= this.queue_len) {
      await new Promise<void>((resolve) => {
        this.pending_tasks.push(resolve);
      });
    }
    this.running++;
    try {
      return await promiseCb();
    } catch (err) {
      throw err;
    } finally {
      const resolve = this.pending_tasks.shift();
      if (resolve) {
        resolve();
      }
      this.running--;
    }
  }
}

const scheduler = new Scheduler(2);

const sleepAndLog = (time: number, ...txt: unknown[]) =>
  new Promise((r) => setTimeout(r, time)).then(() => console.log(...txt));
const sleepAndRaiseLog = (time: number, ...txt: unknown[]) =>
  new Promise((r) => setTimeout(r, time)).then(() => Promise.reject(...txt));

scheduler.add(() => sleepAndLog(10000, "1"));
scheduler.add(() => sleepAndRaiseLog(5000, "2"));
scheduler.add(() => sleepAndLog(3000, "3"));
scheduler.add(() => sleepAndLog(4000, "4"));

Linca · Zero Calorie Drink Shop