1. Js event loop

There are two important concepts in the event loop called macro tasks and micro tasks. Both macro and micro tasks refer to asynchronous tasks. We all know that JavaScript is executed from the top down, and there are two things involved in the execution stack and the task queue. Executing code is placed in the execution stack, and macro and micro tasks are placed in the task queue for execution.

Js is executed from top to bottom first. When asynchronous tasks are encountered, they will be added to the asynchronous task stack. When asynchronous tasks are completed (triggering conditions are met, such as timing time is reached), they will be pushed into the task queue for execution. When the execution of JS stack is completed, check whether there is any task that can be executed in the microtask queue. If there is, take out the task from the queue and put it into the execution stack for execution. After the microtask queue is emptied, check the macro task queue. Fetching tasks from the task queue (emptying the task queue, i.e. removing all or more tasks when there are many waiting to be executed), executing tasks on the execution stack, fetching tasks, executing tasks, is a cycle of events over and over again.

The summary is: perform synchronization code first, then perform microtask, and then check whether the macro task has reached the time, and then execute.

2. What does nextTick do?

Vue.nextTick( [callback, context] )

Parameter: {Function} [callback] {Object} [context] Usage: Executes a deferred callback after the next DOM update loop. Use this method immediately after modifying the data to get the updated DOM.Copy the code

What is nextTick? Next is the next one, tick is the task, which is interpreted as the event cycle, which is the next event cycle. Simply put, the nexttick function is to take a queue and store all the tasks to be executed, and the nexttick (asynchronously) executes those tasks. (Put it in a callback function that fires when the next event loop is entered, not immediately.)

3. NextTick source code implementation

nextTickHandler

Export const nextTick = (function () {/* const callbacks = [] /* */ let pending = false /* A pointer to a function will be pushed to the task queue when the main thread is finished. */ function nextTickHandler () {/* A bit indicating that the function has been pushed to the task queue or the main thread. Is waiting for the current stack to complete. This will eliminate the need to push timerFunc multiple times into the task queue or main thread */ pending = false // Copy the array of callbacks stored const copies = callbacks.slice(0) // Empty array callbacks.length = 0 /* Execute all callback*/ for (let I = 0; i < copies.length; i++) { copies[i]() } }Copy the code

Here nextTick is an immediate function, so it is executed when vue.js is loaded, declares some global variables, and defines a function, nextTickHandler, that executes all the callbacks stored in the callbacks array.


timerFunc

The following is to make a judgment about the current environment in which vue.js is executed, and determine how asynchronously NextTick should defer a task to execute it in the current environment based on the level of support. SetImmediate Mediate (setImmediate), MessageChannel (MessageChannel), Promise (Promise), and setTimeout (None). So the priority is setImmediate > MessageChannel >Promise >setTimeout

// An asynchronous deferring mechanism. // In pre 2.4, we used to use microtasks (Promise/MutationObserver) // but microtasks actually has too high a priority and fires in between // supposedly sequential events (e.g. #4521, #6690) or even between // bubbling of the same event (#6566). Technically setImmediate should be // the ideal choice, but it's not available everywhere; and the only polyfill // that consistently queues the callback after all DOM events triggered in the // same loop is by using MessageChannel. /* istanbul ignore if */ if (typeof setImmediate ! == 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(nextTickHandler) } } else if (typeof MessageChannel ! == 'undefined' && ( isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === '[object MessageChannelConstructor]' )) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage  = nextTickHandler timerFunc = () => { port.postMessage(1) } } else /* istanbul ignore next */ if (typeof Promise ! == 'undefined' && isNative(Promise)) { // use microtask in non-DOM environments, e.g. Weex const p = Promise.resolve() timerFunc = () => { p.then(nextTickHandler) } } else { // fallback to setTimeout timerFunc = () => { setTimeout(nextTickHandler, 0) } }Copy the code

// An asynchronous delay mechanism. // Before 2.4, we used microtasks (Promise/MutationObserver). But microtasks actually have too high a priority and fire between so-called sequential events (e.g. #4521, #6690) or even between bubbling of the same event (#6566). SetImmediate technically, setImmediate should be ideal, but it’s not universally available; After all the DOM events that fire in the same loop, the only polyfill that consistently queues callbacks is the one that uses MessageChannel.

So now nexttick use asynchronous task type priority and is not the same as before, so you can see there are a lot of people online source of nexttick do resolution, but at different time points, the year of the priority of different, not the author write the wrong at the time, but this part of content updates adjusted!!!!!!


queueNextTick

QueueNextTick is queueNextTick. Nexttick is an immediate function, and the immediate function returns queueNextTick, So the usual this.$nexttick is actually calling queueNextTick. It takes two arguments, a CB callback function, and a CTX context.

Is it exactly the same as the $nexttick parameter vue gives

Ok let’s look at what this function does. First, in the callbacks callback group push callback, if cb exists, this callback points to vue instance, CTX default vue instance. Pending is the global variable originally defined. It is a flag bit that indicates whether the function is now pushed to the task queue for execution. If pending is false, change the flag to true, and then execute the timerFunc function. Note, however, that the new callback can still be pushed into the callbacks variable. The callbacks variable will be cleared at the start of execution), timerFunc is defined above, if you don’t remember, you can go back to it. If pending is true, timerFunc() will not be executed while the last event loop is executing. This is done to avoid timerFunc being pushed into the queue more than once. When the event in the task queue is executed by the stack, pending changes back to false.

Return function queueNextTick (cb? : Function, ctx? : Push (() => {if (cb) {try {cb.call(CTX)} catch (e) {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, reject) => { _resolve = resolve }) } }Copy the code

4. Complete nextTICK function code

github.com/vuejs/vue source code at SRC /core/util/env.js

/** * Defer a task to execute it asynchronously. */ export const nextTick = (function () {/* Defer a task to execute it asynchronously */ let pending = false /* A pointer to a function that will be pushed to the task queue when the main thread has finished executing. */ function nextTickHandler () {/* A bit indicating that the function has been pushed to the task queue or the main thread. Is waiting for the current stack to complete. */ pending = false // Copy the array that stores all callbacks Callbacks. Length = 0 /* Execute all callbacks */ for (let I = 0; i < copies.length; I ++) {copies[I]()}} // An asynchronous deferring mechanism. we used to use microtasks (Promise/MutationObserver) // but microtasks actually has too high a priority and fires in between // supposedly sequential events (e.g. #4521, #6690) or even between // bubbling of the same event (#6566). Technically setImmediate should be // the ideal choice, but it's not available everywhere; and the only polyfill // that consistently queues the callback after all DOM events triggered in the // same loop is by using MessageChannel. /* istanbul ignore if */ if (typeof setImmediate ! == 'undefined' && isNative(setImmediate)) { timerFunc = () => { setImmediate(nextTickHandler) } } else if (typeof MessageChannel ! == 'undefined' && ( isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === '[object MessageChannelConstructor]' )) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage  = nextTickHandler timerFunc = () => { port.postMessage(1) } } else /* istanbul ignore next */ if (typeof Promise ! == 'undefined' && isNative(Promise)) { // use microtask in non-DOM environments, e.g. Weex const p = Promise.resolve() timerFunc = () => { p.then(nextTickHandler) } } else { // fallback to setTimeout timerFunc = () => { setTimeout(nextTickHandler, 0)}} /* CTX context */ return function queueNextTick (cb? : Function, ctx? : Push (() => {if (cb) {try {cb.call(CTX)} catch (e) {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, reject) => { _resolve = resolve }) } } })()Copy the code

This article is only for personal learning records, if there are mistakes welcome to point out at any time.