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.

Confused:

How to implement delayed callback

Principle:

  1. One of the hallmarks of the JavaScript language is that it is single-threaded and can only do one thing at a time
  2. JavaScript tasks can be divided into two types, one is synchronous task, the other is asynchronous task
  3. Asynchronous tasks can be roughly divided into macro tasks and micro tasks
  4. All synchronization tasks are executed on the main thread, forming an execution stack
  5. 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”.
  6. Once all synchronization tasks in the Execution stack are completed, the system reads the microtasks in the Task queue, followed by the macro tasks, to see what events are in them. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing.
  7. The main thread repeats step 6 above.

Vue implementation:

Vue mostly uses microtasks in preference, using macro tasks in add events means using vue.nexttick uses macro tasks in events and microtasks in other cases

Vue nextTick macro task implementation

  • Preference for setImmediate detection
if (typeof setImmediate ! == 'undefined' && isNative(setImmediate)) { macroTimerFunc = () => { setImmediate(flushCallbacks) } }Copy the code

SetImmediate Browser support situation

  • Second, check MessageChannel support
else if (typeof MessageChannel ! == 'undefined' && ( isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === '[object MessageChannelConstructor]' )) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage  = flushCallbacks macroTimerFunc = () => { port.postMessage(1) } }Copy the code

MessageChannel browser support

  • Use primitive setTimeout if none of the above is supported
 else {
  /* istanbul ignore next */
  macroTimerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}
Copy the code

Vue nextTick microtask implementation

  • Priority detection Promise
if (typeof Promise ! == 'undefined' && isNative(Promise)) { const p = Promise.resolve() microTimerFunc = () => { 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) } }Copy the code

Promise browser support

  • If promises are not supported, use macro tasks
else {
  // fallback to macro
  microTimerFunc = macroTimerFunc
}
Copy the code

Where are macro tasks and where are micro tasks in VUE?

As you can see from the source code, vue.nexttick defaults to macro tasks when used in DOM events and microtasks when used elsewhere.

In fact, it can be seen from the comments in the source code that Vue initially uses microtask mode, and then there were bugs, which introduced macro task mode

// Here we have async deferring wrappers using both microtasks and (macro) tasks everywhere, but there are some scenarios where // microtasks have too high a priority and fire in between supposedly // sequential events (e.g. #4521, #6690) or even between bubbling of the same // event (#6566). However, using (macro) tasks everywhere also has subtle problems // when state is changed right before repaint (e.g. #6813, out-in transitions). // Here we use microtask by default, but expose a way to force (macro) task when // needed (e.g. in event handlers attached by v-on).Copy the code

More on the Event Loop

Discussion address: record the problems encountered in learning, record the growth