batch-processor.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. "use strict";
  2. var utils = require("./utils");
  3. module.exports = function batchProcessorMaker(options) {
  4. options = options || {};
  5. var reporter = options.reporter;
  6. var asyncProcess = utils.getOption(options, "async", true);
  7. var autoProcess = utils.getOption(options, "auto", true);
  8. if(autoProcess && !asyncProcess) {
  9. reporter && reporter.warn("Invalid options combination. auto=true and async=false is invalid. Setting async=true.");
  10. asyncProcess = true;
  11. }
  12. var batch = Batch();
  13. var asyncFrameHandler;
  14. var isProcessing = false;
  15. function addFunction(level, fn) {
  16. if(!isProcessing && autoProcess && asyncProcess && batch.size() === 0) {
  17. // Since this is async, it is guaranteed to be executed after that the fn is added to the batch.
  18. // This needs to be done before, since we're checking the size of the batch to be 0.
  19. processBatchAsync();
  20. }
  21. batch.add(level, fn);
  22. }
  23. function processBatch() {
  24. // Save the current batch, and create a new batch so that incoming functions are not added into the currently processing batch.
  25. // Continue processing until the top-level batch is empty (functions may be added to the new batch while processing, and so on).
  26. isProcessing = true;
  27. while (batch.size()) {
  28. var processingBatch = batch;
  29. batch = Batch();
  30. processingBatch.process();
  31. }
  32. isProcessing = false;
  33. }
  34. function forceProcessBatch(localAsyncProcess) {
  35. if (isProcessing) {
  36. return;
  37. }
  38. if(localAsyncProcess === undefined) {
  39. localAsyncProcess = asyncProcess;
  40. }
  41. if(asyncFrameHandler) {
  42. cancelFrame(asyncFrameHandler);
  43. asyncFrameHandler = null;
  44. }
  45. if(localAsyncProcess) {
  46. processBatchAsync();
  47. } else {
  48. processBatch();
  49. }
  50. }
  51. function processBatchAsync() {
  52. asyncFrameHandler = requestFrame(processBatch);
  53. }
  54. function clearBatch() {
  55. batch = {};
  56. batchSize = 0;
  57. topLevel = 0;
  58. bottomLevel = 0;
  59. }
  60. function cancelFrame(listener) {
  61. // var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.clearTimeout;
  62. var cancel = clearTimeout;
  63. return cancel(listener);
  64. }
  65. function requestFrame(callback) {
  66. // var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || function(fn) { return window.setTimeout(fn, 20); };
  67. var raf = function(fn) { return setTimeout(fn, 0); };
  68. return raf(callback);
  69. }
  70. return {
  71. add: addFunction,
  72. force: forceProcessBatch
  73. };
  74. };
  75. function Batch() {
  76. var batch = {};
  77. var size = 0;
  78. var topLevel = 0;
  79. var bottomLevel = 0;
  80. function add(level, fn) {
  81. if(!fn) {
  82. fn = level;
  83. level = 0;
  84. }
  85. if(level > topLevel) {
  86. topLevel = level;
  87. } else if(level < bottomLevel) {
  88. bottomLevel = level;
  89. }
  90. if(!batch[level]) {
  91. batch[level] = [];
  92. }
  93. batch[level].push(fn);
  94. size++;
  95. }
  96. function process() {
  97. for(var level = bottomLevel; level <= topLevel; level++) {
  98. var fns = batch[level];
  99. for(var i = 0; i < fns.length; i++) {
  100. var fn = fns[i];
  101. fn();
  102. }
  103. }
  104. }
  105. function getSize() {
  106. return size;
  107. }
  108. return {
  109. add: add,
  110. process: process,
  111. size: getSize
  112. };
  113. }