| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 | type Runner<T = unknown> = ((taskUnit: T) => T | undefined) | undefined;type ThreadOptions = { priority: number };let isLooping = false;function getMapKeys<K extends number | string | symbol>(map: Map<K, unknown>) {  return Array.from(map).map((it) => it[0]);}function workLoop() {  isLooping = true;  const prioritys = getMapKeys(Thread.threadMap).sort((a, b) => b - a);  const curPriority = prioritys[0];  const curPriorityArray = Thread.threadMap.get(curPriority);  curPriorityArray.forEach((thread) => {    thread.samePriorityLength = curPriorityArray.length;    let taskUnit = thread.taskUnit;    thread.start();    while (taskUnit && !thread.shouldYield && !thread.isEmpty) {      taskUnit = thread.taskUnit = thread.exec();    }    if (taskUnit === undefined) {      thread.destroy();    } else {      thread.pause();    }    thread.pause();  });  if (Thread.isAllEmpty()) {    isLooping = false;  } else {    nextTick(workLoop);  }}let channel: MessageChannel;function nextTick(callback) {  if (window.MessageChannel) {    if (!channel) {      channel = new MessageChannel();      channel.port1.onmessage = callback;    }    channel.port2.postMessage("notify");  } else {    setTimeout(callback, 0);  }}class Thread {  static create(    runner: Runner,    taskUnit: unknown = undefined,    options: ThreadOptions  ) {    const thread = new Thread(runner, taskUnit, options);    Thread.threads.push(thread);    const newThreadPriority = thread.options.priority;    const curPriorityArray: Thread[] | undefined =      Thread.threadMap.get(newThreadPriority);    if (curPriorityArray) {      curPriorityArray.push(thread);    } else {      Thread.threadMap.set(newThreadPriority, [thread]);    }    return thread;  }  static threads: Thread[] = [];  static threadMap: Map<number, Thread[]> = new Map();  static isAllEmpty() {    return !Thread.threads.find((thread) => !thread.isEmpty);  }  static yieldInterval = 5;  runner: Runner = undefined;  taskUnit: unknown = undefined;  options: ThreadOptions = {    priority: 0,  };  status: "normal" | "destroyed" = "normal";  samePriorityLength = 0;  private startTime = 0;  constructor(runner: Runner, taskUnit: unknown, options: ThreadOptions) {    this.runner = runner;    this.taskUnit = taskUnit;    this.options = Object.assign(this.options, options);  }  start() {    this.startTime = Date.now();    if (!isLooping) {      workLoop();    }  }  pause() {    this.startTime = 0;  }  exec() {    return this.runner(this.taskUnit);  }  destroy() {    Thread.threads.splice(      Thread.threads.findIndex((it) => it === this),      1    );    const curPriorityArray = Thread.threadMap.get(this.options.priority);    curPriorityArray.splice(      curPriorityArray.findIndex((it) => it === this),      1    );    if (curPriorityArray.length === 0) {      Thread.threadMap.delete(this.options.priority);    }    this.runner = undefined;    this.options = undefined;    this.taskUnit = undefined;  }  get shouldYield() {    return Date.now() > this.startTime + this.yieldInterval;  }  get isEmpty() {    return this.taskUnit === undefined;  }  get yieldInterval() {    return Thread.yieldInterval / this.samePriorityLength;  }}export { Thread };
 |