• home > webfront > ECMAS > javascript >

    如何用JavaScript实现一个并发任务控制的任务调度器

    Author:zhoulujun Date:

    调度器是一种可以控制任务执行顺序的工具,通常用于异步任务的处理。比如前端ajax数据请求,我们需要做限流,就可以用到任务调度器。复杂动画方面就更加了。JavaScript如何实现任务调度器呢

    还是推荐查看react scheduler的经典实现……


    调度器(Scheduler )

    调度器是一种可以控制任务执行顺序的工具,通常用于异步任务的处理。比如:

    • requestAnimationFrame

    • requestIdleCallback

    • setTimeout

    • MessageChannel

    • 微任务

      • MutationObserver

      • Promise

      在Java里面,Timer 和 ScheduledExecutor 都仅能提供基于开始时间与重复间隔的任务调度,不能胜任更加复杂的调度需求。但Spring3.0以后自带的task,即:spring schedule,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

      而前端,React Scheduler 是 react 提供的一个可以独立使用的包,可以单独使用 任务调度器。

    https://github.com/zhoulujun/mini-react/blob/master/docs/schedule/哪些API适合用于任务调度.md

    Scheduler 是 React 提供的调度器,它内部暴露unstable_scheduleCallback(priorityLevel, callback, options)方法给我们调度任务,其中priorityLevel是调度的优先级,callback 是我们的任务,optoins 里面可以通过指定delay延迟执行我们的任务。

    Scheduler 支持任务按优先级排序执行,优先级通过过期时间体现,比如 ImmediatePriority 对应的过期时间是 -1毫秒,需要立即执行。

    var ImmediatePriority = 1; // 对应的过期时间:IMMEDIATE_PRIORITY_TIMEOUT -1毫秒 立即执行
    var UserBlockingPriority = 2; // 对应的过期时间:USER_BLOCKING_PRIORITY_TIMEOUT 250毫秒 后过期
    var NormalPriority = 3; // 对应的过期时间:NORMAL_PRIORITY_TIMEOUT 5000毫秒 后过期
    var LowPriority = 4; // 对应的过期时间:LOW_PRIORITY_TIMEOUT 10000毫秒 后过期
    var IdlePriority = 5; // 对应的过期时间:IDLE_PRIORITY_TIMEOUT maxSigned31BitInt永不过期

    具体参看:https://github.com/zhoulujun/mini-react/blob/master/docs/schedule/scheduler用法详解.md


    设计调度器

    在 JavaScript 中,我们可以使用 Promise 对象和 async/await 函数来实现调度器。

    class TaskScheduler {
      constructor(concurrencyLimit) {
        this.concurrencyLimit = concurrencyLimit; // 最大并发数
        this.queue = []; // 任务队列
        this.runningCount = 0; // 当前正在执行的任务数量
      }
    
      async runTask(blockId) {
        // 模拟异步请求数据的操作
        await fetchBlockData(blockId);
        console.log(`Fetched data for blockId: ${blockId}`);
      }
    
      async scheduleTasks(blockIds) {
        // 将所有任务加入队列
        this.queue.push(...blockIds);
    
        while (this.queue.length > 0) {
          // 如果当前正在执行的任务数量小于并发数限制,执行新任务
          if (this.runningCount < this.concurrencyLimit) {
            const blockId = this.queue.shift();
            if (blockId) {
              this.runningCount++;
              this.runTask(blockId).finally(() => {
                this.runningCount--;
                this.scheduleTasks([]);
              });
            }
          }
          // 等待一段时间后再次检查队列
          await new Promise(resolve => setTimeout(resolve, 1000));
        }
      }
    }
    
    // 模拟异步请求数据的函数
    function fetchBlockData(blockId) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(`Data for blockId ${blockId}`);
        }, Math.random() * 2000); // 模拟耗时 0 到 2 秒的请求
      });
    }
    
    // 测试
    const blockIds = ['1', '2', '3', '4'];
    const scheduler = new TaskScheduler(2); // 最多同时执行两个任务
    
    scheduler.scheduleTasks(blockIds);


    这样的代码,AI写的贼溜了,比如通义千问

    class TaskScheduler {
        private queue: (() => Promise<void>)[] = [];
        private runningTasks: (() => Promise<void>)[] = [];
        private maxConcurrency: number;
    
        constructor(maxConcurrency: number) {
            this.maxConcurrency = maxConcurrency;
        }
    
        // 添加任务到队列
        schedule(task: () => Promise<void>): void {
            this.queue.push(task);
            this.runNext();
        }
    
        // 开始执行队列中的下一个任务
        private async runNext(): Promise<void> {
            // 如果当前运行的任务少于最大并发数,并且队列中还有任务
            while (this.runningTasks.length < this.maxConcurrency && this.queue.length > 0) {
                const task = this.queue.shift();
                if (task) {
                    this.runningTasks.push(task);
                }
            }
    
            // 等待所有当前运行的任务完成
            if (this.runningTasks.length > 0) {
                await Promise.all(this.runningTasks.map(task => task()));
            }
    
            // 移除已完成的任务
            this.runningTasks = [];
    
            // 检查是否有更多任务可执行
            this.runNext();
        }
    }
    
    // 示例用法
    const scheduler = new TaskScheduler(2); // 控制同时并发2个任务
    
    // 假设我们有一些异步任务
    const task1 = async () => {
        console.log('Starting task1');
        await new Promise(resolve => setTimeout(resolve, 1000));
        console.log('Finished task1');
    };
    
    const task2 = async () => {
        console.log('Starting task2');
        await new Promise(resolve => setTimeout(resolve, 500));
        console.log('Finished task2');
    };
    
    const task3 = async () => {
        console.log('Starting task3');
        await new Promise(resolve => setTimeout(resolve, 1500));
        console.log('Finished task3');
    };
    
    const task4 = async () => {
        console.log('Starting task4');
        await new Promise(resolve => setTimeout(resolve, 700));
        console.log('Finished task4');
    };
    
    scheduler.schedule(task1);
    scheduler.schedule(task2);
    scheduler.schedule(task3);
    scheduler.schedule(task4);






    转载本站文章《如何用JavaScript实现一个并发任务控制的任务调度器》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2021_1230_9184.html