Occasionally in everyday development, there are situations where it’s okay to look at the logic and the order of writing (at least as a newbie to me), The problem is solved by adding await setTimeout (100ms) or await NextTick in front of the code that does not achieve the desired effect.

Most of the above scenarios are for listening to see if a DOM is rendered, so why is that? Why does adding a timer or NextTick method solve the problem? Let’s take a look at their mystery

mounted

If you are familiar with this lifecycle, the hook function is triggered when the Vue instance has been mounted. The description of this hook function in the official documentation comes with an additional caveat.

NextTick has been named again, so read on.

NextTick profile

Please Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update.

Defer the callback until after the next DOM update cycle. Use it immediately after you have changed some data to wait for DOM updates.

The source code

vue2.x

Github.com/vuejs/vue/b…

/* @flow */ /* globals MutationObserver */ / English comments are source comments. /** * Perform no operation. * Stubbing args to make Flow happy without leaving useless transpiled code * with ... rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). * export function noop (a?: any, b?: any, c?: any) {} */ import { noop } from 'shared/util' import { handleError } from './error' import { isIE, isIOS, isNative } from './env' export let isUsingMicroTask = false const callbacks = [] let pending = false // FlushCallbacks () {pending = false // Slice return a new array // Why create a new array? Slice (0) callbacks.length = 0 // Cycle through, executing all callbacks one by one on a first-in, first-out basis of the queue data structure. for (let i = 0; i < copies.length; I ++) {copies[I]()}} // Here we have async deferring wrappers using microtasks 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 stops working after triggering a few times... so, if native // Promise is available, we will use it: /* ignoreposition ignore next, $flow-disable-line */ / if (typeof Promise! == 'undefined' && isNative(Promise)) {// New 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} It's more compatible than Promise, but it's a bit cumbersome (guess), so it comes after Promise. else if (! isIE && typeof MutationObserver ! == 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // Use MutationObserver where native Promise is not available, // e.g. PhantomJS, iOS7, Android 4.4 / / (# 6466 MutationObserver is unreliable in IE11) = 1 / / MutationObserver let counter - | MDN Web API interface reference // Create and return a new MutationObserver that is called when the specified DOM changes. const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, {/ / https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserverInit / / set to true to monitor the specified destination node or a child node in the tree node contains the change of character data. There is no default value. characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } (Typeof setImmediate!) Else if (Typeof setImmediate! == '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 { // 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.x

Github.com/vuejs/vue-n…

export function nextTick( this: ComponentPublicInstance | void, fn? : () => void ): Promise<void> { const p = currentFlushPromise || resolvedPromise return fn ? p.then(this ? fn.bind(this) : fn) : p }Copy the code

Q&A

Why create a backup array?

Since callbacks are global variables, only one call to the NextTick function causes the Callbacks to be pushed. When NextTick is nested, FlushCallbacks ensure that the cb input of the current nextTick is executed each time by copying and empting the original array.

Const p = promise.resolve() Const p = new promise (r=>r())

Segmentfault.com/a/119000002…

Why do you choose to downgrade microtasks first and then macro tasks?

Microtasks are preferred because when nextTick is used, it ensures that the microtasks in the queue can be completed before one event loop. If macro task is selected, the callback in nextTick can be executed only after the current task queue is completed.

reference

Github.com/vuejs/vue/b…

Vue-nexttick source code analysis

Comprehensive analysis of vue. nextTick implementation principle

Developer.mozilla.org/zh-CN/docs/…

Developer.mozilla.org/zh-CN/docs/…

Developer.mozilla.org/zh-CN/docs/…

vuejs/vue-next

Vue2 and VUe3 have different implementations of nextTick

nextTick | Vue3