基于async/await的任务并发控制

775 阅读1分钟

前言

遇到一个面试题,挺有意思的,关于异步队列并发控制的,直接看会有点绕,我会分析一下这里面的行为,我相信用其他方式也可以实现。

先上代码

/**
 * 编写一个异步任务调度器
 */
class Scheduler {
  list = []; //用来承载还未执行的异步

  count = 0; //用来计数

  constructor(num) {
    this.num = num; //允许同时运行的异步函数的最大个数
  }

  async add(fn) {
    // 锁
    if (this.count >= this.num) {
      await new Promise((resolve) => {
        this.list.push(resolve);
      });
    }

    this.count++;

    console.log('fn before executed');

    const result = await fn();

    console.log('fn executed');

    this.count--;

    console.log('%c this.list.length %s', 'color: #bfffc8', this.list.length);

    // 如果有任务完成 则后面排队接上
    if (this.list.length > 0) {
      console.log('this.list.shift()();');
      this.list.shift()();
    }

    return result;
  }
}

const schedule = new Scheduler(3); //最多同一时间让它执行3个异步函数

const asyncFactory = (n, time) => {
  return () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(n);
      }, time);
    });
  };
};

schedule.add(asyncFactory(1, 2000)).then((n) => {
  console.log(`异步任务:${n}`);
});
schedule.add(asyncFactory(2, 2000)).then((n) => {
  console.log(`异步任务:${n}`);
});
schedule.add(asyncFactory(3, 2000)).then((n) => {
  console.log(`异步任务:${n}`);
});
schedule.add(asyncFactory(4, 2000)).then((n) => {
  console.log(`异步任务:${n}`);
});
schedule.add(asyncFactory(5, 2000)).then((n) => {
  console.log(`异步任务:${n}`);
});
schedule.add(asyncFactory(6, 2000)).then((n) => {
  console.log(`异步任务:${n}`);
});

原理

我们知道可以同时进行n个任务
首先,将前n个任务异步执行
任务排序大于n的新任务(阻断resolve任务)推到一个队列数组里(这个resolve在没有执行之前会阻断后面代码的执行)
一开始同时执行n个任务,当其中任何一个任务结束之后,就会判断等待队列里是否有任务,如果有任务,就会把等待队列里的第一个任务(阻断resolve任务)拿出来,继续执行
当resolve阻断被fulfilled后,就会执行后面的异步任务