PS: Refer to some blogs for your own review
Implementation principle of Vue nextTick
I’m familiar with the front end of VUE, and I’m sure I’m familiar with nextTick in Vue. When I use it, I know it’s a delay callback, and sometimes it even looks the same as setTimeout. But what’s the difference between it and setTimeout? How did he do it? This paper introduces the implementation of nextTick, to discuss the asynchronous and synchronous JS, micro task and macro task.
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.
// 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
The source code
// 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 */
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
} 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
} 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 {
// Fallback to setTimeout.
timerFunc = () = > {
setTimeout(flushCallbacks, 0)}}Copy the code
And you can see that there are a couple of conditions up there that you can use if you support Promise.
If not, use MutationObserver MDN-MutationObserver MutationObserver is called when the specified DOM changes.
SetImmediate if MutationObserver is not supported setImmediate MDN-setImmediate is used but this feature is only supported by the latest versions of IE and Node. Then there is the final condition and setTimeout if none of these are supported.
After reading this paragraph is also very confused, why do we design it like this? Why is it in such an order? Here we have to discuss JavaScript execution mechanism (Event Loop) & micro task macro task.
JavaScript Runtime mechanism (Eventloop)
Single thread
JS is single-threaded and can only do one thing at a time.
The single thread of JavaScript, relative to its purpose. As a browser scripting language, JavaScript’s primary purpose is to interact with users and manipulate the DOM. This means that it has to be single-threaded, which can cause complex synchronization problems. For example, if there are two threads of JavaScript at the same time, one thread adds content to a DOM node, and the other thread removes that node, which thread should the browser use? So, to avoid complexity, JavaScript has been single-threaded since its inception, and this has been a core feature of the language and will not change.
Synchronous and asynchronous
There are two types of tasks in JS: synchronous and asynchronous.
Synchronous blocking Asynchronous non-blocking.
A synchronous task is a task that is queued for execution on the main thread. The next task can be executed only after the previous task has been executed. For example, alert will block the execution of subsequent tasks and the next task will be executed only after clicking OK. Asynchronous tasks are tasks that do not enter the main thread but enter the task queue. The task queue notifies the main thread that an asynchronous task is ready to execute.
Single threading means that all tasks need to be queued until the first one is finished before the next one can be executed. So there’s the idea of a task queue. Since it is single-threaded, all tasks are executed by the main thread. Asynchronous requests do not open up new threads, but are queued up for execution by the main thread when these asynchronous operations are triggered.
Macro and micro tasks
JS task is divided into macro task and micro task.
Macrotask: setTimeout, setInterval, setImmediate, I/O, UI Rendering
Microtasks: Promise.then, Process.nexttick, MutationObserver, queneMicrotask(start a microtask)
The macro tasks are executed sequentially and the browser renders the page between each macro task. The browser renders the page after the execution of one task and before the execution of the next task (Task -> Render -> Task ->…).
Microtasks are typically tasks that need to be executed immediately after the execution of the current task, such as feedback on a series of actions, or tasks that need to be executed asynchronously without assigning a new task to reduce the performance overhead. The microtask queue executes as soon as no other JS code is executing in the execution stack and each macro task completes. If a new microtask is added to the microtask queue during the execution of the microtask, the new microtask will be added to the end of the queue and will be executed later.
When to use microtasks
The microtask executes later than the code in the Call Stack(macro task) of the current event loop and encounters the event handler and timer callback functions
Reasons for using microtasks
Reducing user-perceived latency in operations ensures consistency of task order, even when the result or data is available synchronously for batch operations
Knowing the execution sequence of macro tasks and microtasks, you can see why nextTick preferentially uses Promise and MutationObserver because they are microtasks that execute immediately when the execution stack is idle, and their response speed is faster than setTimeout because there is no need to wait for rendering. SetImmediate and setTimeout are macro tasks that wait for a rendering, task-> Render -> Task, before execution begins.