aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/core/observer/scheduler.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vue/src/core/observer/scheduler.js')
-rw-r--r--node_modules/vue/src/core/observer/scheduler.js148
1 files changed, 148 insertions, 0 deletions
diff --git a/node_modules/vue/src/core/observer/scheduler.js b/node_modules/vue/src/core/observer/scheduler.js
new file mode 100644
index 00000000..fce86e5f
--- /dev/null
+++ b/node_modules/vue/src/core/observer/scheduler.js
@@ -0,0 +1,148 @@
+/* @flow */
+
+import type Watcher from './watcher'
+import config from '../config'
+import { callHook, activateChildComponent } from '../instance/lifecycle'
+
+import {
+ warn,
+ nextTick,
+ devtools
+} from '../util/index'
+
+export const MAX_UPDATE_COUNT = 100
+
+const queue: Array<Watcher> = []
+const activatedChildren: Array<Component> = []
+let has: { [key: number]: ?true } = {}
+let circular: { [key: number]: number } = {}
+let waiting = false
+let flushing = false
+let index = 0
+
+/**
+ * Reset the scheduler's state.
+ */
+function resetSchedulerState () {
+ index = queue.length = activatedChildren.length = 0
+ has = {}
+ if (process.env.NODE_ENV !== 'production') {
+ circular = {}
+ }
+ waiting = flushing = false
+}
+
+/**
+ * Flush both queues and run the watchers.
+ */
+function flushSchedulerQueue () {
+ flushing = true
+ let watcher, id
+
+ // Sort queue before flush.
+ // This ensures that:
+ // 1. Components are updated from parent to child. (because parent is always
+ // created before the child)
+ // 2. A component's user watchers are run before its render watcher (because
+ // user watchers are created before the render watcher)
+ // 3. If a component is destroyed during a parent component's watcher run,
+ // its watchers can be skipped.
+ queue.sort((a, b) => a.id - b.id)
+
+ // do not cache length because more watchers might be pushed
+ // as we run existing watchers
+ for (index = 0; index < queue.length; index++) {
+ watcher = queue[index]
+ 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] > MAX_UPDATE_COUNT) {
+ warn(
+ 'You may have an infinite update loop ' + (
+ watcher.user
+ ? `in watcher with expression "${watcher.expression}"`
+ : `in a component render function.`
+ ),
+ watcher.vm
+ )
+ break
+ }
+ }
+ }
+
+ // keep copies of post queues before resetting state
+ const activatedQueue = activatedChildren.slice()
+ const updatedQueue = queue.slice()
+
+ resetSchedulerState()
+
+ // call component updated and activated hooks
+ callActivatedHooks(activatedQueue)
+ callUpdatedHooks(updatedQueue)
+
+ // devtool hook
+ /* istanbul ignore if */
+ if (devtools && config.devtools) {
+ devtools.emit('flush')
+ }
+}
+
+function callUpdatedHooks (queue) {
+ let i = queue.length
+ while (i--) {
+ const watcher = queue[i]
+ const vm = watcher.vm
+ if (vm._watcher === watcher && vm._isMounted) {
+ callHook(vm, 'updated')
+ }
+ }
+}
+
+/**
+ * Queue a kept-alive component that was activated during patch.
+ * The queue will be processed after the entire tree has been patched.
+ */
+export function queueActivatedComponent (vm: Component) {
+ // setting _inactive to false here so that a render function can
+ // rely on checking whether it's in an inactive tree (e.g. router-view)
+ vm._inactive = false
+ activatedChildren.push(vm)
+}
+
+function callActivatedHooks (queue) {
+ for (let i = 0; i < queue.length; i++) {
+ queue[i]._inactive = true
+ activateChildComponent(queue[i], true /* true */)
+ }
+}
+
+/**
+ * Push a watcher into the watcher queue.
+ * Jobs with duplicate IDs will be skipped unless it's
+ * pushed when the queue is being flushed.
+ */
+export function queueWatcher (watcher: Watcher) {
+ const id = watcher.id
+ if (has[id] == null) {
+ has[id] = true
+ if (!flushing) {
+ queue.push(watcher)
+ } else {
+ // if already flushing, splice the watcher based on its id
+ // if already past its id, it will be run next immediately.
+ let i = queue.length - 1
+ while (i > index && queue[i].id > watcher.id) {
+ i--
+ }
+ queue.splice(i + 1, 0, watcher)
+ }
+ // queue the flush
+ if (!waiting) {
+ waiting = true
+ nextTick(flushSchedulerQueue)
+ }
+ }
+}