前端面试题二:Promise的并发

63 阅读2分钟

题目

function timeout(fn, time) {
  return () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        fn();
        resolve();
      }, time);
    });
  };
}

task.add(
  timeout(() => {
    console.log("任务一");
  }, 1000)
); // 1000毫秒后输出'任务一'
task.add(
  timeout(() => {
    console.log("任务二");
  }, 2000)
); // 2000毫秒后输出'任务二'
task.add(
  timeout(() => {
    console.log("任务三");
  }, 2000)
); // 3000毫秒后输出'任务三'

task.add(
  timeout(() => {
    console.log("任务四");
  }, 3000)
); // 5000毫秒后输出'任务四'

分析

  1. 1.任务一和任务二是按照时间顺序执行的;
  2. 2.任务三延迟了一秒执行,任务四延迟了两秒执行;
  3. 3.根据时间推算发现任务三是在任务一执行完毕后执行,刚好结束时是第三秒;
  4. 4.根据时间推算发现任务四是任务二执行完毕后,任务四开始执行刚好结束时是第五秒
  5. 5.可以知道任务一和任务二同步执行,任务三和任务四是在前面任务执行完毕后,按顺序补位继续执行的结果。
  6. 6.总结上面可以知道,其实就是考察 Promise 的并行执行,上面的并行数量是 2;

实现步骤

1.创建一个类,要有最大并发数,存储任务的数组

class Task {
  constructor(limit) {
    this.limit = limit; // 最大并发数
    this.queue = []; // 任务存储队列
  }
}

2.要有 add 方法能够不断添加任务函数

class Task {
  constructor(limit) {
    this.limit = limit; // 最大并发数
    this.queue = []; // 任务存储队列
  }
  add(promise) {
    this.queue.push(promise);
  }
}

3.添加任务以后,要立刻执行给定任务,并检查“当前并发数”是否大于“最大并发数”,保证同时只能执行limit任务函数;同时当前任务队列的数量要大于0,保证不会导致意外出错

class Task {
  constructor(limit) {
    this.limit = limit; // 最大并发数
    this.queue = []; // 任务存储队列
    this.parallel = 0; // 当前并发数
  }
  add(promise) {
    this.queue.push(promise);
    this._run();
  }
   _run() {
    if (this.queue.length > 0 && this.parallel < this.limit) {
      const first = this.queue.shift();
      first()
      this.parallel += 1;
    }
  }
}

4.在第3步只是保证了有任务函数就立刻按照最大并发数执行;但是超过并发数的任务并没有执行;所以需要在任务函数执行完毕后,无论成功还是失败都要执行一次_run函数,并且把“当前并发数”减一

class Task {
  constructor(limit) {
    this.limit = limit; // 最大并发数
    this.queue = []; // 任务存储队列
    this.parallel = 0; // 当前并发数
  }
  add(promise) {
    this.queue.push(promise);
    this._run();
  }
  _run() {
    if (this.queue.length > 0 && this.parallel < this.limit) {
      const first = this.queue.shift();
      first().finally(() => {
        this.parallel -= 1;
        this._run();
      });
      this.parallel += 1;
    }
  }
}

const task = new Task(2);

测试结果

function timeout(fn, time) {
  return () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        fn();
        resolve();
      }, time);
    });
  };
}

class Task {
  constructor(limit) {
    this.limit = limit; // 最大并发数
    this.queue = []; // 任务存储队列
    this.parallel = 0; // 当前并发数
  }
  add(promise) {
    this.queue.push(promise);
    this._run();
  }
  _run() {
    if (this.queue.length > 0 && this.parallel < this.limit) {
      const first = this.queue.shift();
      first().finally(() => {
        this.parallel -= 1;
        this._run();
      });
      this.parallel += 1;
    }
  }
}

const task = new Task(2);

task.add(
  timeout(() => {
    console.log("任务一");
  }, 1000)
); // 1000毫秒后输出'任务一'
task.add(
  timeout(() => {
    console.log("任务二");
  }, 2000)
); // 2000毫秒后输出'任务二'
task.add(
  timeout(() => {
    console.log("任务三");
  }, 2000)
); // 3000毫秒后输出'任务三'

task.add(
  timeout(() => {
    console.log("任务四");
  }, 3000)
); // 5000毫秒后输出'任务四'