123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142 |
- 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();
- }
- });
- 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: "running" | "destroyed" | "paused" = "paused";
- 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.status = "running";
- this.startTime = Date.now();
- if (!isLooping) {
- workLoop();
- }
- }
- pause() {
- this.status = "paused";
- 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;
- this.status = "destroyed";
- }
- get shouldYield() {
- return Date.now() > this.startTime + this.yieldInterval;
- }
- get isEmpty() {
- return this.taskUnit === undefined;
- }
- get yieldInterval() {
- return Thread.yieldInterval / this.samePriorityLength;
- }
- }
- export { Thread };
|