batcher.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  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 circular = {}
  13. var waiting = false
  14. var internalQueueDepleted = false
  15. /**
  16. * Reset the batcher's state.
  17. */
  18. function resetBatcherState () {
  19. queue = []
  20. userQueue = []
  21. has = {}
  22. circular = {}
  23. waiting = internalQueueDepleted = false
  24. }
  25. /**
  26. * Flush both queues and run the watchers.
  27. */
  28. function flushBatcherQueue () {
  29. runBatcherQueue(queue)
  30. internalQueueDepleted = true
  31. runBatcherQueue(userQueue)
  32. // dev tool hook
  33. /* istanbul ignore if */
  34. if (process.env.NODE_ENV !== 'production') {
  35. if (_.inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
  36. window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('flush')
  37. }
  38. }
  39. resetBatcherState()
  40. }
  41. /**
  42. * Run the watchers in a single queue.
  43. *
  44. * @param {Array} queue
  45. */
  46. function runBatcherQueue (queue) {
  47. // do not cache length because more watchers might be pushed
  48. // as we run existing watchers
  49. for (var i = 0; i < queue.length; i++) {
  50. var watcher = queue[i]
  51. var id = watcher.id
  52. has[id] = null
  53. watcher.run()
  54. // in dev build, check and stop circular updates.
  55. if (process.env.NODE_ENV !== 'production' && has[id] != null) {
  56. circular[id] = (circular[id] || 0) + 1
  57. if (circular[id] > config._maxUpdateCount) {
  58. queue.splice(has[id], 1)
  59. _.warn(
  60. 'You may have an infinite update loop for watcher ' +
  61. 'with expression: ' + watcher.expression
  62. )
  63. }
  64. }
  65. }
  66. }
  67. /**
  68. * Push a watcher into the watcher queue.
  69. * Jobs with duplicate IDs will be skipped unless it's
  70. * pushed when the queue is being flushed.
  71. *
  72. * @param {Watcher} watcher
  73. * properties:
  74. * - {Number} id
  75. * - {Function} run
  76. */
  77. exports.push = function (watcher) {
  78. var id = watcher.id
  79. if (has[id] == null) {
  80. // if an internal watcher is pushed, but the internal
  81. // queue is already depleted, we run it immediately.
  82. if (internalQueueDepleted && !watcher.user) {
  83. watcher.run()
  84. return
  85. }
  86. // push watcher into appropriate queue
  87. var q = watcher.user ? userQueue : queue
  88. has[id] = q.length
  89. q.push(watcher)
  90. // queue the flush
  91. if (!waiting) {
  92. waiting = true
  93. _.nextTick(flushBatcherQueue)
  94. }
  95. }
  96. }