🐶 来来来,整一个简单的串行任务调度器(支持顺序执行及暂停任务)

1,344 阅读5分钟

前言:

在计算机系统中,任务调度是一项关键的管理任务的技术。它通过合理地安排和管理任务的执行顺序和时间,以提高系统的性能、资源利用率和响应时间。
本文将实现一个基于 JavaScript 实现的串行任务调度器,探讨其作用、原理。
附带简单的测试用例

实际效果

image.png

效果动图

动画666123456712312.gif

代码需求

实现一个任务调度器,提供串行执行任务异步任务处理中断重启任务队列获取队列执行结果的能力

代码实现

class TPromise {
 // 任务列表
 taskList = [];
 // 任务停止标识
 stop = false;
 // 宏观promise
 macroscopicPromise = "";
 // 任务添加
 addTask(fn, pause = false) {
   this.taskList.push({
     fn,
     pause,
   });
 }
 // 宏观任务执行
 macroscopicExecuteTask(d = "") {
   this.macroscopicPromise = new Promise((resolve, reject) => {
     this.executeTask(d, resolve, reject);
   });
 }
 // 真实任务执行
 executeTask(d = "", resolve, reject) {
   if (this.taskList.length && !this.stop) {
     this.resolveTask(this.taskList[0].fn, d)
       .then((res) => {
         // 如果当前任务需要暂停,更新队列执行状态,同时resolve宏观promise
         if (this.taskList[0].pause) {
           this.stopCh();
           resolve(res);
         }
         // 消费当前任务
         this.taskList.shift();
         // 如果任务全部消费完,同样resolve宏观promise
         if(this.taskList.length == 0) {
           resolve(res);
         }
         this.executeTask(res, resolve, reject);
       })
       .catch(() => {
         this.stopCh();
       });
   }
 }
 // 任务重启
 nextTask() {
   this.macroscopicPromise.then((res) => {
     this.stopCh(false);
     this.macroscopicExecuteTask(res);
   });
 }
 // 队列状态变更
 stopCh(bool = true) {
   this.stop = bool;
 }
 // 任务解析
 resolveTask(fn, d) {
   let resP = new Promise((resolve, reject) => {
     if (fn instanceof Promise) {
       // 任务是否Promise,是则resolve其Promise结果
       fn.then((res) => {
         resolve(res);
       }).then((rej) => {
         reject(rej);
       });
     }  else if (
       (typeof fn === "object" || typeof fn === "function") &&
       fn !== null
     ) {
       let then = fn.then;
       if (then) {
         // 任务是否thenable,是则执行其then方法,函数决定是否resolve
         then.call(fn, d, resolve, reject);
       } else if(typeof fn === "function") {
         // 是否正常函数,是则执行函数,函数决定是否resolve
         fn(d, resolve, reject);
       } else {
         // 若fn为非thenable对象直接返回
         resolve(fn)
       }
     } else {
       // 若fn为基本数据类型或数组等直接返回
       resolve(fn)
     }
   });
   return resP;
 }
 // 获取队列执行结果(队列暂停/队列消费完结束)
 then(resolveFn, rejectFn) {
   if(this.macroscopicPromise instanceof Promise) {
     return this.macroscopicPromise.then(resolveFn, rejectFn)
   } else {
     return Promise.resolve('').then(resolveFn, rejectFn)
   }
 }
}

代码解析

上述代码实现了一个名为 TPromise 的任务调度器。它采用了基于 Promise 的异步编程模型,通过添加任务到任务列表中,并按照添加顺序依次执行任务。具体的原理如下:

  1. 任务添加:通过 addTask 方法,可以向任务列表中添加任务。每个任务由一个函数 fn 表示,可以根据需要传入参数pause选择是否在执行该任务后暂停任务调度器。
  2. 宏观任务执行:通过macroscopicExecuteTask方法,将macroscopicPromise赋值为一个promise,并在promise中执行executeTask方法,即真实任务执行函数,同时传入macroscopicPromise的resolve和reject函数,只有当任务队列中出现暂停标识的任务时才会执行resolve方法,通过这一步去实现异步任务的暂停。
  3. 任务执行:通过 executeTask 方法,任务调度器会按照任务列表中的顺序执行任务。如果任务列表不为空且任务调度器未被停止,则会执行任务列表中的第一个任务,当成功之后,会校验当前是否需要暂停任务,如果需要暂停则调用stopCh更新任务调度器为停止状态,并执行macroscopicPromise的resolve方法返回结果。 无论是否需要暂停,都会消费当前执行的任务,然后判断当前任务是否消费完,消费完则执行macroscopicPromise的resolve方法返回结果。 无论是否消费完,都会基于暂存的执行结果继续递归执行 executeTask同时将接收的macroscopicPromise的resolve和reject函数作为参数继续传入。
  4. 任务状态更新:通过 stopCh 方法,可以控制任务调度器的停止状态。传入 true 表示停止任务调度器,传入 false 表示恢复任务调度器的执行。
  5. 任务重启执行:通过 nextTask 方法重启任务,因为宏观任务执行会将macroscopicPromise赋值为一个promise,这个promise只有当任务暂停的时候才会resolve结果,所以在macroscopicPromise的then操作中重新调用stopCh方法和macroscopicExecuteTask方法,可以将当前任务执行状态改成启动状态,并且基于暂停的任务结果,重新执行剩下的任务,直到出现另一个暂停标识为止。
  6. 任务解析:通过 resolveTask 方法,任务调度器会解析传入的Task。 resolveTask函数返回的是一个 Promise 对象,支持解析promise、function、thenable、对象、数组及基础数据类型,resolve对应的执行结果
  7. then:then方法接收成功回调和失败回调,首先判断this.macroscopicPromise是否是一个Promise实例,如果是,就调用this.macroscopicPromise的then方法,如果this.macroscopicPromise不是一个Promise实例,就创建一个已解决状态的Promise实例,值为一个空字符串(Promise.resolve('')),然后调用这个Promise实例的then方法,并将resolveFn和rejectFn作为参数传递进去,然后返回then生成的promise,then的目的主要是提供入口,使得开发者可以得到任务队列在暂停或结束时的结果

简单用例:

function text(params, res, rej) {
  return new Promise(() => {
    console.log("开始", params);
    setTimeout(() => {
      console.log("结束", params);
      res(params);
    }, params);
  });
}
function text1(params, resolve, reject) {
  console.log("测试");
  resolve(5000);
}

T.addTask(text);
T.addTask(text1, true);
T.addTask(text);
T.macroscopicExecuteTask(2000);
T.then((res)=>{console.log(res)},()=>{}) // 5000
T.nextTask();
T.then((res)=>{console.log(res)},()=>{}) // 5000

结尾:

总的来说,这个基于JavaScript的任务调度器实现了串行执行任务、异步任务处理以及中断任务队列执行等功能。
我们也可以在此基础上进行扩展,添加更多的功能和特性,使任务调度器更加强大和实用。

手动@柚子呀,给我灵感🛫️

代码缺陷:TPromise的then函数在异步处理中仍有缺陷,后续更新修复...