几个变量、函数
callbacks
callbacks数组用于保存nextTick创建的函数(在这个函数的内部会执行nextTick传入的函数)
flushCallbacks
flushCallbacks方法用于遍历、执行callbacks中的函数。首先会通过callbacks.splice复制回调函数数组,并清空callbacks
function flushCallbacks() {
// 表示正在执行callbacks中的函数
pending = false
// 复制callbacks中的函数,并清空callbacks
const copies = callbacks.slice(0)
callbacks.length = 0
// 遍历、执行函数
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
timerFunc
timerFunc方法用于触发flushCallbacks方法的执行。它会根据运行环境采取不同的触发方式,优先级按照先后顺序如下:
- 支持原生的Promise:通过Promise.resolve().then(flushCallbacks)实现异步
- 支持原生的MutationObserver:通过new MutationObserver(flushCallbacks)实现异步
- 支持原生的setImmediate:通过setImmediate(flushCallbacks)实现异步
- 以上都不支持:通过setTimeout(flushCallbacks)实现异步
let timerFunc
if (typeof Promise !== 'undefined' && isNative(Promise)) {
// Cond: 支持原生的Promise
const p = Promise.resolve()
// timerFunc的内部通过Promise.resolve().then()触发flushCallbacks
timerFunc = () => {
p.then(flushCallbacks)
}
} else if (
typeof MutationObserver !== 'undefined' &&
(isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
// Cond: 支持原生的MutationObserver
let counter = 1
// 创建一个MutationObserver实例
// 当观察的节点变化时,触发flashCallbacks
const observer = new MutationObserver(flushCallbacks)
// 创建观察的文本节点
const textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
// timerFunc的内部通过修改观察的节点的数据,触发flushCallbacks
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// Cond: 支持原生的setImmediate
// timerFunc的内部通过setImmediat触发flushCallbacks
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// Cond: 以上条件都不满足
// timerFunc的内部通过setTimeout触发flushCallbacks
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
nextTick
nextTick用于在vue的数据更新之后,执行传入的函数。
为何能保证函数在状态更新之后被执行呢?
A:因为状态的更新是同步的,nextTick会将传入的函数放在微任务(根据运行环境的不同,也可能是宏任务)中执行。这样就能保证函数在数据更新之后被执行
function nextTick(cb?: (...args: any[]) => any, ctx?: object) {
// 如果执行nextTick没有传入函数,将返回一个Promise
// _resolve就会用于保存Promise的resolve方法,用于改变Promise的状态
let _resolve
// 创建一个新的函数,并放进callbacks中
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e: any) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
// 将Promise的状态改为fulfilled
// 值为更新状态后的上下文
_resolve(ctx)
}
})
if (!pending) {
// Cond: 没有在执行flushCallbacks
// 则执行timerFunc
pending = true
timerFunc()
}
// 如果没有传入函数,则返回一个Promise实例
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
// 创建Promise实例,并将resolve指向_resolve
_resolve = resolve
})
}
}