thread.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. let isLooping = false;
  2. function getMapKeys(map) {
  3. return Array.from(map).map((it) => it[0]);
  4. }
  5. function workLoop() {
  6. isLooping = true;
  7. const prioritys = getMapKeys(Thread.threadMap).sort((a, b) => b - a);
  8. const curPriority = prioritys[0];
  9. const curPriorityArray = Thread.threadMap.get(curPriority);
  10. curPriorityArray.forEach((thread) => {
  11. thread.samePriorityLength = curPriorityArray.length;
  12. let taskUnit = thread.taskUnit;
  13. thread.start();
  14. while (taskUnit && !thread.shouldYield && !thread.isEmpty) {
  15. taskUnit = thread.taskUnit = thread.exec();
  16. }
  17. if (taskUnit === undefined) {
  18. thread.destroy();
  19. }
  20. else {
  21. thread.pause();
  22. }
  23. });
  24. if (Thread.isAllEmpty()) {
  25. isLooping = false;
  26. }
  27. else {
  28. nextTick(workLoop);
  29. }
  30. }
  31. let channel;
  32. function nextTick(callback) {
  33. if (window.MessageChannel) {
  34. if (!channel) {
  35. channel = new MessageChannel();
  36. channel.port1.onmessage = callback;
  37. }
  38. channel.port2.postMessage("notify");
  39. }
  40. else {
  41. setTimeout(callback, 0);
  42. }
  43. }
  44. class Thread {
  45. static create(runner, taskUnit = undefined, options) {
  46. const thread = new Thread(runner, taskUnit, options);
  47. Thread.threads.push(thread);
  48. const newThreadPriority = thread.options.priority;
  49. const curPriorityArray = Thread.threadMap.get(newThreadPriority);
  50. if (curPriorityArray) {
  51. curPriorityArray.push(thread);
  52. }
  53. else {
  54. Thread.threadMap.set(newThreadPriority, [thread]);
  55. }
  56. return thread;
  57. }
  58. static isAllEmpty() {
  59. return !Thread.threads.find((thread) => !thread.isEmpty);
  60. }
  61. constructor(runner, taskUnit, options) {
  62. this.runner = undefined;
  63. this.taskUnit = undefined;
  64. this.options = {
  65. priority: 0,
  66. };
  67. this.status = "paused";
  68. this.samePriorityLength = 0;
  69. this.startTime = 0;
  70. this.runner = runner;
  71. this.taskUnit = taskUnit;
  72. this.options = Object.assign(this.options, options);
  73. }
  74. start() {
  75. this.status = "running";
  76. this.startTime = Date.now();
  77. if (!isLooping) {
  78. workLoop();
  79. }
  80. }
  81. pause() {
  82. this.status = "paused";
  83. this.startTime = 0;
  84. }
  85. exec() {
  86. return this.runner(this.taskUnit);
  87. }
  88. destroy() {
  89. Thread.threads.splice(Thread.threads.findIndex((it) => it === this), 1);
  90. const curPriorityArray = Thread.threadMap.get(this.options.priority);
  91. curPriorityArray.splice(curPriorityArray.findIndex((it) => it === this), 1);
  92. if (curPriorityArray.length === 0) {
  93. Thread.threadMap.delete(this.options.priority);
  94. }
  95. this.runner = undefined;
  96. this.options = undefined;
  97. this.taskUnit = undefined;
  98. this.status = "destroyed";
  99. }
  100. get shouldYield() {
  101. return Date.now() > this.startTime + this.yieldInterval;
  102. }
  103. get isEmpty() {
  104. return this.taskUnit === undefined;
  105. }
  106. get yieldInterval() {
  107. return Thread.yieldInterval / this.samePriorityLength;
  108. }
  109. }
  110. Thread.threads = [];
  111. Thread.threadMap = new Map();
  112. Thread.yieldInterval = 5;
  113. export { Thread };