Before diving into the $nextTick principle, we need to take a quick look at the browser’s event loop mechanism.
Browser event loop mechanism
As we all know, JavaScript is single-threaded, which means that when JavaScript executes code, there is only one main thread to handle all the tasks. What about asynchronous code, such as I/O operations? That’s the event loop we’re going to talk about.
Execution stack and task queue
When JavaScript executes code, it executes code from the top down.
When a synchronization task is encountered, the system adds the synchronization task to the execution stack.
When an asynchronous task is encountered, it is not executed immediately. Instead, it is added to the task queue and executed after all synchronous tasks in the stack are completed.
Macro tasks and Micro Tasks
Asynchronous tasks are divided into macro tasks and micro tasks.
Asynchronous macro tasks include: setTimeout, setInterval, setImmediate, I/O, AND UI Rendering.
Asynchronous microtasks include: Process. nextTick, Promise, MutationObserver, Object.observe.
Event loop
- Process synchronization tasks and add them to the execution stack in turn
- Wait for the synchronization tasks in the execution stack to complete processing, processing the task queue
- Perform all microtasks in sequence
- Do the necessary UI rendering
- Execute asynchronous code in macro tasks
- Start the next event cycle
$nextTick
Vue’s $nextTick also uses an event loop to execute asynchronous tasks after data updates and view renders are complete.
Let’s take a look at the main function of $nextTick:
.function flushCallbacks () {
// Execute all callbacks
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
...
export function nextTick (cb? :Function, ctx? :Object) {
let _resolve
// Add the callback function to the array
callbacks.push(() = > {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')}}else if (_resolve) {
_resolve(ctx)
}
})
if(! pending) { pending =true
// Wait for the view to finish rendering to execute all callbacks
timerFunc()
}
if(! cb &&typeof Promise! = ='undefined') {
return new Promise(resolve= > {
_resolve = resolve
})
}
}
Copy the code
TimerFunc selects the appropriate asynchronous task execution method based on browser support.
As we know from the event loop, the necessary UI rendering is done after the microtask, followed by the asynchronous macro task.
That is, if we want to execute callbacks as soon as possible after rendering the view, we are better off using microtasks to handle asynchronous tasks, or macro tasks if the browser doesn’t support them.
Then let’s see how timerFunc is defined:
let timerFunc
if (typeof Promise! = ='undefined' && isNative(Promise)) {
// If promises are supported, use promises to perform asynchronous tasks
const p = Promise.resolve()
timerFunc = () = > {
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if(! isIE &&typeofMutationObserver ! = ='undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
/ / use MutationObserver
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)) {
/ / use setImmediate
timerFunc = () = > {
setImmediate(flushCallbacks)
}
} else {
/ / use setTimeout
timerFunc = () = > {
setTimeout(flushCallbacks, 0)}}Copy the code
conclusion
$nextTick uses an event loop to execute all callbacks after the view is rendered.
Furthermore, in order to execute callbacks as soon as possible after rendering the view, $nextTick renders the method for executing asynchronous tasks according to browser compatibility, first checking whether microtasks (Promise, MutationObserver) can be used, Failing that, use setImmediate (setTimeout).
😉 if the article is helpful to you, please help to light up the thumb next to the bai, any questions are welcome to discuss in the comments section.