Basic usage

Usage:

A deferred callback is performed after the next DOM update loop ends. Use this method immediately after modifying the data to get the updated DOM.

Cite examples from the official website

// Modify the data
vm.msg = 'Hello'
// DOM has not been updated yet
Vue.nextTick(function () {
  // DOM is updated
})

// Use as a Promise (new since 2.1.0, see hints below)
Vue.nextTick()
  .then(function () {
    // DOM is updated
  })

Copy the code

Source contrast

Vue 2.6.10

src/core/util/next-tick.js

/* @flow */
/* globals MutationObserver */

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

export let isUsingMicroTask = false

const callbacks = []
let pending = false
// Array of microtask functions are executed in sequence
function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

// Here we have async deferring wrappers using microtasks.
// In 2.5 we use (macro) tasks In combination with microtasks.
// However, it has subtle problems when state is changed right before repaint
// (e.g. #6813, out-in transitions).
// Also, using (macro) tasks in event handler would cause some weird behaviors
// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
// So we now use microtasks everywhere, again.
// A major drawback of this tradeoff is that there are some scenarios
// where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566).
let timerFunc

// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */

// Check whether promises are supported natively
if (typeof Promise! = ='undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () = > {
    p.then(flushCallbacks)
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
  
  // Determine whether MutationObserver is supported
} else if(! isIE &&typeofMutationObserver ! = ='undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  // Use MutationObserver where native Promise is not available,
  PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () = > {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
  // Determine whether setImmediate is supported
} else if (typeofsetImmediate ! = ='undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Technically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () = > {
    setImmediate(flushCallbacks)
  }
} else {
// setTimeout is not supported
  // Fallback to setTimeout.
  timerFunc = () = > {
    setTimeout(flushCallbacks, 0)}}export function nextTick (cb? :Function, ctx? :Object) {
  let _resolve
  callbacks.push(() = > {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')}}else if (_resolve) {
      _resolve(ctx)
    }
  })
  if(! pending) { pending =true
    timerFunc()
  }
  // $flow-disable-line
  if(! cb &&typeof Promise! = ='undefined') {
    return new Promise(resolve= > {
      _resolve = resolve
    })
  }
}

Copy the code

vue3

Path packages/runtime – core/SRC/scheduler. The ts

export function nextTick(fn? : () = >void) :Promise<void> {
  return fn ? p.then(fn) : p
}
Copy the code

Analysis summary

By comparison, vue2’s nextTick source code is compatible with many browser versions by defining different timerFunc functions using the following methods to listen and execute microtask queues in an unknown environment

  1. Promise
  2. MutationObserver listens for DOM changes
  3. setImmediate
  4. setTimeout

So vue3’s source code directly uses Promise, using the TASK invocation mechanism of the JS engine itself to achieve dom update invocation.

Quote Ruan Yifeng teacher’s explanation of javascript asynchronous operation mechanism

Asynchronous execution works as follows. (The same is true for synchronous execution, since it can be considered asynchronous execution without asynchronous tasks.)

(1) All synchronization tasks are executed on the main thread, forming an execution context stack.

(2) In addition to the main thread, there is a task queue. Whenever an asynchronous task has a result, an event is placed in the “task queue”.

(3) Once all synchronization tasks in the “execution stack” are completed, the system reads the “task queue” to see what events are in it. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing.

(4) The main thread repeats step 3 above.

Personal analysis and summary:

The nextTick is not called until the main thread is completed. The Promise, MutationObserver, setImmediate, etc., is that asynchronous mediate is not invoked until the main thread is completed. SetTimeout is set in different situations to generate asynchronous tasks.