男人一事无成时的温柔是最廉价的segmentfault
在理解nextTick以前,咱们先要了解js的运行机制浏览器
js执行是单线程的,基于事件循环,事件循环大体分为如下几个步骤:bash
(1)全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)。异步
(2)主线程以外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件,能够看出,这个任务队列主要存放异步任务的函数
(3)一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。ui
(4)主线程不断重复上面的第三步。this
它的源码很简单,
源码:src/core/util/next-tick.js线程
export let isUsingMicroTask = false
/* 存放异步执行的回调 */
const callbacks = []
/* 标记位,若是已经有timerFunc被推送到任务队列中去则不须要重复推送 */
let pending = false
/* 循环执行异步回调 */
function flushCallbacks () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
/* 一个函数指针,指向函数将被推送到任务队列中,等到主线程任务执行完时,任务队列中的timeFunc将被调用 */
let timerFunc
/* 兼容Promise的浏览器 */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks)
}
/* 执行微任务标志位 */
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
/* 不支持Promise则使用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 (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
/* 不支持Promise和MutationObserver则使用setImmediate */
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
/* 不然都使用setTimeout */
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
/* 延迟一个任务使其异步执行,在下一个tick时执行,这个函数的做用是在task或者microtask中推入一个timerFunc,在当前调用栈执行完之后以此执行直到执行到timerFunc,目的是延迟到当前调用栈执行完之后执行 */
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)
}
})
/* 已经有timerFunc被推送到任务队列中去则不须要重复推送 */
if (!pending) {
pending = true
timerFunc()
}
/* nextTick没有传回调函数时,返回一个Promise,咱们能够nextTick().then(() => {}) */
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
复制代码
解析:能够看出,nextTick在内部对环境作了兼容,首先会检测是否支持微任务:Promise,支持的话则把咱们传入的回调押入队列中在下一个tick中等待执行(flushCallbacks依次执行回调),若不支持则使用MutationObserver,如以上二者都不支持则使用宏任务:setImmediate和setTimeout。
最后若是咱们没有给nextTick传入回调,则能够nextTick().then(() => {})跳到then逻辑中。指针
结合上一节的 setter 分析,咱们了解到数据的变化到 DOM 的从新渲染是一个异步过程,发生在下一个 tick。这就是咱们平时在开发的过程当中,好比从服务端接口去获取数据的时候,数据作了修改,若是咱们的某些方法去依赖了数据修改后的 DOM 变化,咱们就必须在 nextTick 后执行。好比下面的伪代码:
getData(res).then(()=>{
this.xxx = res.data
this.$nextTick(() => {
// 这里咱们能够获取变化后的 DOM
})
})
复制代码