batcher.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. var _ = require('./util')
  2. var config = require('./config')
  3. // we have two separate queues: one for directive updates
  4. // and one for user watcher registered via $watch().
  5. // we want to guarantee directive updates to be called
  6. // before user watchers so that when user watchers are
  7. // triggered, the DOM would have already been in updated
  8. // state.
  9. var queue = []
  10. var userQueue = []
  11. var has = {}
  12. var waiting = false
  13. var flushing = false
  14. var internalQueueDepleted = false
  15. /**
  16. * Reset the batcher's state.
  17. */
  18. function reset () {
  19. queue = []
  20. userQueue = []
  21. has = {}
  22. waiting = flushing = internalQueueDepleted = false
  23. }
  24. /**
  25. * Flush both queues and run the jobs.
  26. */
  27. function flush () {
  28. flushing = true
  29. run(queue)
  30. internalQueueDepleted = true
  31. run(userQueue)
  32. reset()
  33. }
  34. /**
  35. * Run the jobs in a single queue.
  36. *
  37. * @param {Array} queue
  38. */
  39. function run (queue) {
  40. // do not cache length because more jobs might be pushed
  41. // as we run existing jobs
  42. for (var i = 0; i < queue.length; i++) {
  43. queue[i].run()
  44. }
  45. }
  46. /**
  47. * Push a job into the job queue.
  48. * Jobs with duplicate IDs will be skipped unless it's
  49. * pushed when the queue is being flushed.
  50. *
  51. * @param {Object} job
  52. * properties:
  53. * - {String|Number} id
  54. * - {Function} run
  55. */
  56. exports.push = function (job) {
  57. var id = job.id
  58. if (!id || !has[id] || flushing) {
  59. if (!has[id]) {
  60. has[id] = 1
  61. } else {
  62. has[id]++
  63. // detect possible infinite update loops
  64. if (has[id] > config._maxUpdateCount) {
  65. _.warn(
  66. 'You may have an infinite update loop for the ' +
  67. 'watcher with expression: "' + job.expression + '".'
  68. )
  69. return
  70. }
  71. }
  72. // A user watcher callback could trigger another
  73. // directive update during the flushing; at that time
  74. // the directive queue would already have been run, so
  75. // we call that update immediately as it is pushed.
  76. if (flushing && !job.user && internalQueueDepleted) {
  77. job.run()
  78. return
  79. }
  80. ;(job.user ? userQueue : queue).push(job)
  81. if (!waiting) {
  82. waiting = true
  83. _.nextTick(flush)
  84. }
  85. }
  86. }