关于大厂常见面试题之"并发限制的异步调度器 "

lxf2023-05-17 01:36:01

关于最近频繁遇到的一个面试题 手写并发异步调度

流程分析:

整个的完整执行流程:

  1. 起始1、2两个任务开始执行;
  2. 500ms时,2任务执行完毕,输出2,任务3开始执行;
  3. 800ms时,3任务执行完毕,输出3,任务4开始执行;
  4. 1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行;
  5. 1200ms时,4任务执行完毕,输出4;

当资源不足时将任务加入等待队列,当资源足够时,将等待队列中的任务取出执行

在调度器中一般会有一个等待队列queue,存放当资源不够时等待执行的任务
具有并发数据限制,假设通过max设置允许同时运行的任务,还需要count表示当前正在执行的任务数量
当需要执行一个任务A时,先判断count==max 如果相等说明任务A不能执行,应该被阻塞,阻塞的任务放进queue中,等待任务调度器管理
如果count<max说明正在执行的任务数没有达到最大容量,那么count++执行任务A,执行完毕后count--
此时如果queue中有值,说明之前有任务因为并发数量限制而被阻塞,现在count<max,任务调度器会将对头的任务弹出执行

class Scheduler {
  constructor(max) {
    this.max = max;
    this.count = 0; // 用来记录当前正在执行的异步函数
    this.queue = new Array(); // 表示等待队列
  }
  async add(promiseCreator) {
    /*
        此时count已经满了,不能执行本次add需要阻塞在这里,将resolve放入队列中等待唤醒,
        等到count<max时,从队列中取出执行resolve,执行,await执行完毕,本次add继续
        */
    if (this.count >= this.max) {
      await new Promise((resolve, reject) => this.queue.push(resolve));
    }

    this.count++;
    let res = await promiseCreator();
    this.count--;
    if (this.queue.length) {
      // 依次唤醒add
      // 若队列中有值,将其resolve弹出,并执行
      // 以便阻塞的任务,可以正常执行
      this.queue.shift()();
    }
    return res;
  }
}

const timeout = time =>
  new Promise(resolve => {
    setTimeout(resolve, time);
  });

const scheduler = new Scheduler(2);

const addTask = (time, order) => {
  //add返回一个promise,参数也是一个promise
  scheduler.add(() => timeout(time)).then(() => console.log(order));
};
  
  addTask(1000, '1');
  addTask(500, '2');
  addTask(300, '3');
  addTask(400, '4');
  
// output: 2 3 1 4

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!