JavaScript实现带并发限制的异步调度器

 更新时间:2024年03月26日 09:09:59   作者:翰玥  
这篇文章主要为大家详细介绍了如何基于JS实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有N个,感兴趣的小伙伴可以了解下

题目

实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有N个。完善下面代码中的Scheduler类,使得以下程序能正确输出:

class Scheduler {
  add(promiseCreator) { ... }
  // ...
}

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

const scheduler = new Scheduler(n)
const addTask = (time, order) => {
  scheduler.add(() => timeout(time)).then(() => console.log(order))
}

addTask(1000, '1')  // 任务1
addTask(500, '2') // 任务2
addTask(300, '3') // 任务3
addTask(400, '4')  // 任务4

// 打印顺序是:2 3 1 4

题目分析

假设N为2,也就是保证同时运行的任务有2个。那么在执行addTask 4步操作之后,整体的流程应该是这样的。

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

为什么会出现这样的结果?我们来具体分析一下

首先连续执行了4次addTask,由于只能同时运行的任务有2个,所以,任务1和任务2将直接运行,任务1将在1000ms之后运行,任务2将在500ms之后运行,所以,任务2肯定会比任务1执行的快。当任务2执行完毕之后,输出2。紧接着执行任务3,此时任务1执行也就经过了500ms,还有500ms没有执行完,而任务3只需要300ms就执行完毕,所以任务3也会比任务1执行的快。又过了300ms(共计过了800ms)任务3执行完毕,输出3。任务4开始执行,任务4需要400ms执行完毕,而任务1目前只需要200ms,所以任务1会比任务4先执行,200ms之后(共计1000ms)任务1执行完毕,输出1,在过了200ms(共计1200ms),任务4执行完毕,输出4。

下面我们用图来表示一下

知道了这道题目具体要干啥了,下面就来看看代码是如何实现的

代码实现

直接上完整代码好了~

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

这块代码中我们主要加了add部分。

首先我们来分析一下 Scheduler这个类。max表示同时可以执行任务的最大数量。count用来记录当前正在执行的异步函数。每次addTask都会通过scheduler.add添加一个异步任务。

进入add函数中,首先需要做的事情是当前已经正在执行的任务有没有到达最大的任务数。

如果没有达到最大的任务数(比如刚开始的加入任务一和任务二,此时任务是空的),每次执行await promiseCreator();这一步的时候,使用async await,当promiseCreator没有执行完毕的时候,会阻塞后面的任务。所以当前两个任务被addTask加入的时候,执行add的时候,都会阻塞后面的任务。而我们的四个任务连续被加入的。当add任务三和任务四的时候,发现此时count已经满了,所以需要阻塞在这里,将resolve放入队列中等待唤醒吗,具体什么时候被唤醒呢?当前面的任务有任何一个执行完毕之后,就可以被唤醒。这里使用queue来维护resolveadd任务三和任务四的时候,会先后给queue推入这两个promiseresolve

经过500ms,任务二会先执行完毕,也就是await promiseCreator();执行完毕之后,打印2,然后继续之后后续的代码,此时从queue里面将第一个resolve弹出,并执行。执行之后,任务3也就不再阻塞了,将继续执行await promiseCreator();.

再经过300ms任务三先执行完毕之后(任务还在继续执行中),打印3,然后继续之后后续的代码,此时从queue里面将resolve弹出,并执行。执行之后,任务4也就不再阻塞了,将继续执行await promiseCreator();.

再经过200ms任务一终于执行完毕之后,打印1,然后继续之后后续的代码,此时queue里面已经是空的了

再经过200ms任务四执行完毕,打印4

到此这篇关于JavaScript实现带并发限制的异步调度器的文章就介绍到这了,更多相关JavaScript异步调度器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 常用DOM整理

    常用DOM整理

    js在操作DOM中存在着许多跨浏览器方面的坑,本文花了我将近一周的时间整理,我将根据实例整理那些大大小小的“坑”。
    2015-06-06
  • jQuery实现手风琴特效

    jQuery实现手风琴特效

    这篇文章主要为大家详细介绍了前端js实现手风琴效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-01-01
  • 详解如何准确判断JavaScript中的数据类型

    详解如何准确判断JavaScript中的数据类型

    JavaScript中,我们经常需要判断数据类型以便于正确地处理数据,本文将介绍JavaScript中的数据类型判断技术,包括typeof操作符、instanceof操作符、Object.prototype.toString方法以及ES6新增的一些数据类型判断方法,需要的朋友可以参考下
    2023-08-08
  • 使用js实现数据格式化

    使用js实现数据格式化

    这篇文章主要介绍了使用javascript实现数据格式化为字符串,非常的实用,这里推荐给有相同需求的小伙伴。
    2014-12-12
  • Echarts.js实现水滴球和海洋效果

    Echarts.js实现水滴球和海洋效果

    这篇文章介绍了Echarts.js实现水滴球和海洋效果的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • 别了 JavaScript中的isXX系列

    别了 JavaScript中的isXX系列

    我们很容易被漂亮的代码吸引,也不知不觉的在自己的代码库中加入这些。却没有冷静的想过它们的优劣。这不,我就收集了一系列形如 是否为……? 的判断的boolean函数
    2012-08-08
  • Bootstrap每天必学之前端开发框架

    Bootstrap每天必学之前端开发框架

    Bootstrap每天必学之前端开发框架,今天这篇文章就带着大家了解认识当下最流行的前端开发框架Bootstrap,bootstrap深受大家喜爱的原因到底是什么呢,我们一探究竟。
    2015-11-11
  • 原生JS生成九宫格

    原生JS生成九宫格

    这篇文章主要为大家详细介绍了原生JS生成九宫格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • ES6学习教程之对象的扩展详解

    ES6学习教程之对象的扩展详解

    这篇文章主要给大家介绍了ES6中对象扩展的相关资料,文中介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-05-05
  • 微信小程序自定义可滚动的弹出框

    微信小程序自定义可滚动的弹出框

    这篇文章主要为大家详细介绍了微信小程序自定义可滚动的弹出框,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07

最新评论