前端面试题之任务调度场景

56 阅读1分钟

更多文章可看专栏:juejin.cn/column/7423…

multiRequest并发请求

function multiRequest(urls = [], maxNum) {
  // 请求总数量
  const len = urls.length;
  // 根据请求数量创建一个数组来保存请求的结果
  const result = new Array(len).fill(false);
  // 当前完成的数量
  let count = 0;

  return new Promise((resolve, reject) => {
    // 请求maxNum个
    while (count < maxNum) {
      next();
    }
    function next() {
      let current = count++;
      // 处理边界条件
      if (current >= len) {
        // 请求全部完成就将promise置为成功状态, 然后将result作为promise值返回
        !result.includes(false) && resolve(result);
        return;
      }
      const url = urls[current];
      console.log(`开始 ${current}`, new Date().toLocaleString());
      fetch(url)
        .then((res) => {
          // 保存请求结果
          result[current] = res;
          console.log(`完成 ${current}`, new Date().toLocaleString());
        })
        .catch((err) => {
          console.log(`结束 ${current}`, new Date().toLocaleString());
          result[current] = err;
        })
        .finally(() => {
          // 请求没有全部完成, 就递归
          if (current < len) {
            next();
          }
        });
    }
  });
}

Scheduler异步调度

/**
 * 题目: JS实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。完善代码中Scheduler类,使得以下程序能正确输出
 * 条件: 只能修改Sheduler
 **/
class Scheduler {
    constructor() {
        this.max = 2;
        this.cache = [];
        this.task = [];
    }

    add(promiseCreator) {
        return new Promise((resolve) => {
            promiseCreator.resolve = resolve;
            if (this.task.length < this.max) {
                this.runTask(promiseCreator);
            } else {
                this.cache.push(promiseCreator);
            }
        })
    }

    runTask(promiseCreator) {
        this.task.push(promiseCreator);
        promiseCreator().then(() => {
            promiseCreator.resolve();      //promiseCreator异步任务完成以后,再调用外层Promise的resolve以便add().then()的执行
            var index = this.task.indexOf(promiseCreator);
            this.task.splice(index, 1);   //从正在进行的任务队列中删除
            if (this.cache.length > 0) {
                this.runTask(this.cache.shift());
            }
        })

    }
}

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

const scheduler = new Scheduler();
const addTask = (time, order) => {
    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
  // 一开始,1、2两个任务进入队列
  // 500ms时,2完成,输出2,任务3进队
  // 800ms时,3完成,输出3,任务4进队
  // 1000ms时,1完成,输出1
  // 1200ms时,4完成,输出4

LazyMan懒汉模型

class LazyMan {
  tasks = []; // 任务列表
  name = ""; // 懒汉
  constructor(name) {
    this.name = name;
    // 采用异步,确保所有的链式调用函数都添加到列表中后,自动执行第一个任务
    setTimeout(() => {
      this.next();
    }, 0);
  }
  // 执行下一个任务,每次执行完都会从任务列表中删除
  next() {
    const task = this.tasks.shift(); // 从头部删除,会直接影响原数组
    task && task();
  }
  // 阻塞,使得程序延后time执行
  sleep(time) {
    // 定义一个任务函数
    const task = () => {
      console.log(`sleep ${time}s 开始`);
      setTimeout(() => {
        console.log(`sleep ${time}s 结束`);
        this.next(); // 执行完当前函数后,继续执行下一个任务,直到任务执行完毕
      }, time * 1000); // 使用定时器来模拟实现阻塞功能
    };
    // 添加到任务列表中
    this.tasks.push(task);
    // 返回当前对象,保持链式调用
    return this;
  }
  eat(food) {
    // 定义一个任务函数
    const task = () => {
      console.log(`eat ${food}`);
      this.next(); // 执行完当前函数后,继续执行下一个任务,直到任务执行完毕
    };
    // 添加到任务列表中
    this.tasks.push(task);
    // 返回当前对象,保持链式调用
    return this;
  }
  done() {
    const task = () => {
      //   console.log(`当前待处理的任务:`);
      //   console.log(`${this.tasks}`);
      this.next();
    };
    this.tasks.unshift(task);
    return this;
  }
}
const lazyMan = new LazyMan("张三");
lazyMan.eat("dinner").sleep(10).eat("super").sleep(5);