import { inBrowser, warn, nextTick } from './util/index' import config from './config' // we have two separate queues: one for directive updates // and one for user watcher registered via $watch(). // we want to guarantee directive updates to be called // before user watchers so that when user watchers are // triggered, the DOM would have already been in updated // state. var queue = [] var userQueue = [] var has = {} var circular = {} var waiting = false var internalQueueDepleted = false /** * Reset the batcher's state. */ function resetBatcherState () { queue = [] userQueue = [] has = {} circular = {} waiting = internalQueueDepleted = false } /** * Flush both queues and run the watchers. */ function flushBatcherQueue () { runBatcherQueue(queue) internalQueueDepleted = true runBatcherQueue(userQueue) // dev tool hook /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production') { if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('flush') } } resetBatcherState() } /** * Run the watchers in a single queue. * * @param {Array} queue */ function runBatcherQueue (queue) { // do not cache length because more watchers might be pushed // as we run existing watchers for (var i = 0; i < queue.length; i++) { var watcher = queue[i] var id = watcher.id has[id] = null watcher.run() // in dev build, check and stop circular updates. if (process.env.NODE_ENV !== 'production' && has[id] != null) { circular[id] = (circular[id] || 0) + 1 if (circular[id] > config._maxUpdateCount) { queue.splice(has[id], 1) warn( 'You may have an infinite update loop for watcher ' + 'with expression: ' + watcher.expression ) } } } } /** * Push a watcher into the watcher queue. * Jobs with duplicate IDs will be skipped unless it's * pushed when the queue is being flushed. * * @param {Watcher} watcher * properties: * - {Number} id * - {Function} run */ export function pushWatcher (watcher) { var id = watcher.id if (has[id] == null) { // if an internal watcher is pushed, but the internal // queue is already depleted, we run it immediately. if (internalQueueDepleted && !watcher.user) { watcher.run() return } // push watcher into appropriate queue var q = watcher.user ? userQueue : queue has[id] = q.length q.push(watcher) // queue the flush if (!waiting) { waiting = true nextTick(flushBatcherQueue) } } }