scheduler.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. import {ImageRequestTask} from './image_request_task.js';
  5. /**
  6. * Scheduler for ImageRequestTask objects. Fetches tasks from a queue and
  7. * processes them synchronously, taking into account priorities. The highest
  8. * priority is 0.
  9. * @constructor
  10. */
  11. export function Scheduler() {
  12. /**
  13. * List of tasks waiting to be checked. If these items are available in
  14. * cache, then they are processed immediately after starting the scheduler.
  15. * However, if they have to be downloaded, then these tasks are moved
  16. * to pendingTasks_.
  17. *
  18. * @type {Array<ImageRequestTask>}
  19. * @private
  20. */
  21. this.newTasks_ = [];
  22. /**
  23. * List of pending tasks for images to be downloaded.
  24. * @type {Array<ImageRequestTask>}
  25. * @private
  26. */
  27. this.pendingTasks_ = [];
  28. /**
  29. * List of tasks being processed.
  30. * @type {Array<ImageRequestTask>}
  31. * @private
  32. */
  33. this.activeTasks_ = [];
  34. /**
  35. * Map of tasks being added to the queue, but not finalized yet. Keyed by
  36. * the ImageRequestTask id.
  37. * @type {Object<string, ImageRequestTask>}>
  38. * @private
  39. */
  40. this.tasks_ = {};
  41. /**
  42. * If the scheduler has been started.
  43. * @type {boolean}
  44. * @private
  45. */
  46. this.started_ = false;
  47. }
  48. /**
  49. * Maximum download tasks to be run in parallel.
  50. * @type {number}
  51. * @const
  52. */
  53. Scheduler.MAXIMUM_IN_PARALLEL = 5;
  54. /**
  55. * Adds a task to the internal priority queue and executes it when tasks
  56. * with higher priorities are finished. If the result is cached, then it is
  57. * processed immediately once the scheduler is started.
  58. *
  59. * @param {ImageRequestTask} task A task to be run
  60. */
  61. Scheduler.prototype.add = function(task) {
  62. if (!this.started_) {
  63. this.newTasks_.push(task);
  64. this.tasks_[task.getId()] = task;
  65. return;
  66. }
  67. // Enqueue the tasks, since already started.
  68. this.pendingTasks_.push(task);
  69. this.sortPendingTasks_();
  70. this.continue_();
  71. };
  72. /**
  73. * Removes a task from the scheduler (if exists).
  74. * @param {string} taskId Unique ID of the task.
  75. */
  76. Scheduler.prototype.remove = function(taskId) {
  77. const task = this.tasks_[taskId];
  78. if (!task) {
  79. return;
  80. }
  81. // Remove from the internal queues with pending tasks.
  82. const newIndex = this.newTasks_.indexOf(task);
  83. if (newIndex != -1) {
  84. this.newTasks_.splice(newIndex, 1);
  85. }
  86. const pendingIndex = this.pendingTasks_.indexOf(task);
  87. if (pendingIndex != -1) {
  88. this.pendingTasks_.splice(pendingIndex, 1);
  89. }
  90. // Cancel the task.
  91. task.cancel();
  92. delete this.tasks_[taskId];
  93. };
  94. /**
  95. * Starts handling tasks.
  96. */
  97. Scheduler.prototype.start = function() {
  98. this.started_ = true;
  99. // Process tasks added before scheduler has been started.
  100. this.pendingTasks_ = this.newTasks_;
  101. this.sortPendingTasks_();
  102. this.newTasks_ = [];
  103. // Start serving enqueued tasks.
  104. this.continue_();
  105. };
  106. /**
  107. * Sorts pending tasks by priorities.
  108. * @private
  109. */
  110. Scheduler.prototype.sortPendingTasks_ = function() {
  111. this.pendingTasks_.sort(function(a, b) {
  112. return a.getPriority() - b.getPriority();
  113. });
  114. };
  115. /**
  116. * Processes pending tasks from the queue. There is no guarantee that
  117. * all of the tasks will be processed at once.
  118. *
  119. * @private
  120. */
  121. Scheduler.prototype.continue_ = function() {
  122. // Run only up to MAXIMUM_IN_PARALLEL in the same time.
  123. while (this.pendingTasks_.length &&
  124. this.activeTasks_.length < Scheduler.MAXIMUM_IN_PARALLEL) {
  125. const task = this.pendingTasks_.shift();
  126. this.activeTasks_.push(task);
  127. // Try to load from cache. If doesn't exist, then download.
  128. task.loadFromCacheAndProcess(
  129. this.finish_.bind(this, task), function(currentTask) {
  130. currentTask.downloadAndProcess(this.finish_.bind(this, currentTask));
  131. }.bind(this, task));
  132. }
  133. };
  134. /**
  135. * Handles finished tasks.
  136. *
  137. * @param {ImageRequestTask} task Finished task.
  138. * @private
  139. */
  140. Scheduler.prototype.finish_ = function(task) {
  141. const index = this.activeTasks_.indexOf(task);
  142. if (index < 0) {
  143. console.warn('ImageRequestTask not found.');
  144. }
  145. this.activeTasks_.splice(index, 1);
  146. delete this.tasks_[task.getId()];
  147. // Continue handling the most important tasks (if started).
  148. if (this.started_) {
  149. this.continue_();
  150. }
  151. };