let isLooping = false; function getMapKeys(map) { 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; 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, taskUnit = undefined, options) { const thread = new Thread(runner, taskUnit, options); Thread.threads.push(thread); const newThreadPriority = thread.options.priority; const curPriorityArray = Thread.threadMap.get(newThreadPriority); if (curPriorityArray) { curPriorityArray.push(thread); } else { Thread.threadMap.set(newThreadPriority, [thread]); } return thread; } static isAllEmpty() { return !Thread.threads.find((thread) => !thread.isEmpty); } constructor(runner, taskUnit, options) { this.runner = undefined; this.taskUnit = undefined; this.options = { priority: 0, }; this.status = "paused"; this.samePriorityLength = 0; this.startTime = 0; 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; } } Thread.threads = []; Thread.threadMap = new Map(); Thread.yieldInterval = 5; export { Thread };