Queue.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. /**
  2. * A queue
  3. * @param {Object} options
  4. * Available options:
  5. * - delay: number When provided, the queue will be flushed
  6. * automatically after an inactivity of this delay
  7. * in milliseconds.
  8. * Default value is null.
  9. * - max: number When the queue exceeds the given maximum number
  10. * of entries, the queue is flushed automatically.
  11. * Default value of max is Infinity.
  12. * @constructor Queue
  13. */
  14. function Queue(options) {
  15. // options
  16. this.delay = null;
  17. this.max = Infinity;
  18. // properties
  19. this._queue = [];
  20. this._timeout = null;
  21. this._extended = null;
  22. this.setOptions(options);
  23. }
  24. /**
  25. * Update the configuration of the queue
  26. * @param {Object} options
  27. * Available options:
  28. * - delay: number When provided, the queue will be flushed
  29. * automatically after an inactivity of this delay
  30. * in milliseconds.
  31. * Default value is null.
  32. * - max: number When the queue exceeds the given maximum number
  33. * of entries, the queue is flushed automatically.
  34. * Default value of max is Infinity.
  35. */
  36. Queue.prototype.setOptions = function (options) {
  37. if (options && typeof options.delay !== 'undefined') {
  38. this.delay = options.delay;
  39. }
  40. if (options && typeof options.max !== 'undefined') {
  41. this.max = options.max;
  42. }
  43. this._flushIfNeeded();
  44. };
  45. /**
  46. * Extend an object with queuing functionality.
  47. * The object will be extended with a function flush, and the methods provided
  48. * in options.replace will be replaced with queued ones.
  49. * @param {Object} object
  50. * @param {Object} options
  51. * Available options:
  52. * - replace: Array.<string>
  53. * A list with method names of the methods
  54. * on the object to be replaced with queued ones.
  55. * - delay: number When provided, the queue will be flushed
  56. * automatically after an inactivity of this delay
  57. * in milliseconds.
  58. * Default value is null.
  59. * - max: number When the queue exceeds the given maximum number
  60. * of entries, the queue is flushed automatically.
  61. * Default value of max is Infinity.
  62. * @return {Queue} Returns the created queue
  63. */
  64. Queue.extend = function (object, options) {
  65. var queue = new Queue(options);
  66. if (object.flush !== undefined) {
  67. throw new Error('Target object already has a property flush');
  68. }
  69. object.flush = function () {
  70. queue.flush();
  71. };
  72. var methods = [{
  73. name: 'flush',
  74. original: undefined
  75. }];
  76. if (options && options.replace) {
  77. for (var i = 0; i < options.replace.length; i++) {
  78. var name = options.replace[i];
  79. methods.push({
  80. name: name,
  81. original: object[name]
  82. });
  83. queue.replace(object, name);
  84. }
  85. }
  86. queue._extended = {
  87. object: object,
  88. methods: methods
  89. };
  90. return queue;
  91. };
  92. /**
  93. * Destroy the queue. The queue will first flush all queued actions, and in
  94. * case it has extended an object, will restore the original object.
  95. */
  96. Queue.prototype.destroy = function () {
  97. this.flush();
  98. if (this._extended) {
  99. var object = this._extended.object;
  100. var methods = this._extended.methods;
  101. for (var i = 0; i < methods.length; i++) {
  102. var method = methods[i];
  103. if (method.original) {
  104. object[method.name] = method.original;
  105. }
  106. else {
  107. delete object[method.name];
  108. }
  109. }
  110. this._extended = null;
  111. }
  112. };
  113. /**
  114. * Replace a method on an object with a queued version
  115. * @param {Object} object Object having the method
  116. * @param {string} method The method name
  117. */
  118. Queue.prototype.replace = function(object, method) {
  119. var me = this;
  120. var original = object[method];
  121. if (!original) {
  122. throw new Error('Method ' + method + ' undefined');
  123. }
  124. object[method] = function () {
  125. // create an Array with the arguments
  126. var args = [];
  127. for (var i = 0; i < arguments.length; i++) {
  128. args[i] = arguments[i];
  129. }
  130. // add this call to the queue
  131. me.queue({
  132. args: args,
  133. fn: original,
  134. context: this
  135. });
  136. };
  137. };
  138. /**
  139. * Queue a call
  140. * @param {function | {fn: function, args: Array} | {fn: function, args: Array, context: Object}} entry
  141. */
  142. Queue.prototype.queue = function(entry) {
  143. if (typeof entry === 'function') {
  144. this._queue.push({fn: entry});
  145. }
  146. else {
  147. this._queue.push(entry);
  148. }
  149. this._flushIfNeeded();
  150. };
  151. /**
  152. * Check whether the queue needs to be flushed
  153. * @private
  154. */
  155. Queue.prototype._flushIfNeeded = function () {
  156. // flush when the maximum is exceeded.
  157. if (this._queue.length > this.max) {
  158. this.flush();
  159. }
  160. // flush after a period of inactivity when a delay is configured
  161. clearTimeout(this._timeout);
  162. if (this.queue.length > 0 && typeof this.delay === 'number') {
  163. var me = this;
  164. this._timeout = setTimeout(function () {
  165. me.flush();
  166. }, this.delay);
  167. }
  168. };
  169. /**
  170. * Flush all queued calls
  171. */
  172. Queue.prototype.flush = function () {
  173. while (this._queue.length > 0) {
  174. var entry = this._queue.shift();
  175. entry.fn.apply(entry.context || entry.fn, entry.args || []);
  176. }
  177. };
  178. module.exports = Queue;