js实现异步并发调度器的两种思路

50 阅读1分钟

1.任务队列

常规思路,利用队列记录等待执行的异步任务,递归调用执行函数即可

const request = (delay) =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`请求完成: ${delay}s`);
    }, delay * 1000);
  });

type Task = () => Promise<unknown>;
const controller = (count: number) => {
  const taskQue: Task[] = [];
  let requestCount = 0;

  const run = () => {
    if (taskQue.length && requestCount < count) {
      const curRequest = taskQue.shift()!;
      requestCount += 1;
      curRequest().then((res) => {
        requestCount -= 1;
        console.log(res);
        run();
      });
    }
  };

  const addTask = (fn: Task) => {
    taskQue.push(fn);
    run();
  };

  return {
    run,
    addTask,
  };
};

/* 最多并发2个任务 */
const request4 = controller(2);

console.log("haahaaha");
request4.addTask(() => request(1));
request4.addTask(() => request(1));
request4.addTask(() => request(1));
request4.addTask(() => request(1));

2. 异步函数唤醒队列

当请求大于容量时,利用异步函数的await阻塞异步函数执行,同时将唤醒执行的resolve方法放入等待队列,当有任务完成或失败时调用resolve方法唤醒被阻塞的异步操作

function createAsyncWorker(capacity) {
  /* 等待队列 */
  const taskQue = [];
  let requestCount = 0;

  const executor = async (asyncFn) => {
    /* 如果请求数大于容量,利用await挂起,将唤醒resolve方法加入队列 */
    if (requestCount >= capacity) {
      await new Promise((resolve) => taskQue.push(resolve));
    }
    requestCount += 1;
    /*  任务reject时也需要开始下一个任务,此处不能用await,await接收reject的promise时会抛出异常 */
    let promise = asyncFn();
    promise
      .then(() => {
        requestCount -= 1;
        if (taskQue.length) {
          taskQue.shift()();
        }
      })
      .catch(() => {
        requestCount -= 1;
        if (taskQue.length) {
          taskQue.shift()();
        }
      });
    return promise;
  };

  return executor;
}