thread.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  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. thread.pause();
  24. });
  25. if (Thread.isAllEmpty()) {
  26. isLooping = false;
  27. }
  28. else {
  29. nextTick(workLoop);
  30. }
  31. }
  32. let channel;
  33. function nextTick(callback) {
  34. if (window.MessageChannel) {
  35. if (!channel) {
  36. channel = new MessageChannel();
  37. channel.port1.onmessage = callback;
  38. }
  39. channel.port2.postMessage("notify");
  40. }
  41. else {
  42. setTimeout(callback, 0);
  43. }
  44. }
  45. class Thread {
  46. static create(runner, taskUnit = undefined, options) {
  47. const thread = new Thread(runner, taskUnit, options);
  48. Thread.threads.push(thread);
  49. const newThreadPriority = thread.options.priority;
  50. const curPriorityArray = Thread.threadMap.get(newThreadPriority);
  51. if (curPriorityArray) {
  52. curPriorityArray.push(thread);
  53. }
  54. else {
  55. Thread.threadMap.set(newThreadPriority, [thread]);
  56. }
  57. return thread;
  58. }
  59. static isAllEmpty() {
  60. return !Thread.threads.find((thread) => !thread.isEmpty);
  61. }
  62. constructor(runner, taskUnit, options) {
  63. this.runner = undefined;
  64. this.taskUnit = undefined;
  65. this.options = {
  66. priority: 0,
  67. };
  68. this.status = "normal";
  69. this.samePriorityLength = 0;
  70. this.startTime = 0;
  71. this.runner = runner;
  72. this.taskUnit = taskUnit;
  73. this.options = Object.assign(this.options, options);
  74. }
  75. start() {
  76. this.startTime = Date.now();
  77. if (!isLooping) {
  78. workLoop();
  79. }
  80. }
  81. pause() {
  82. this.startTime = 0;
  83. }
  84. exec() {
  85. return this.runner(this.taskUnit);
  86. }
  87. destroy() {
  88. Thread.threads.splice(Thread.threads.findIndex((it) => it === this), 1);
  89. const curPriorityArray = Thread.threadMap.get(this.options.priority);
  90. curPriorityArray.splice(curPriorityArray.findIndex((it) => it === this), 1);
  91. if (curPriorityArray.length === 0) {
  92. Thread.threadMap.delete(this.options.priority);
  93. }
  94. this.runner = undefined;
  95. this.options = undefined;
  96. this.taskUnit = undefined;
  97. }
  98. get shouldYield() {
  99. return Date.now() > this.startTime + this.yieldInterval;
  100. }
  101. get isEmpty() {
  102. return this.taskUnit === undefined;
  103. }
  104. get yieldInterval() {
  105. return Thread.yieldInterval / this.samePriorityLength;
  106. }
  107. }
  108. Thread.threads = [];
  109. Thread.threadMap = new Map();
  110. Thread.yieldInterval = 5;
  111. export { Thread };