深入了解网址
https://mp.weixin.qq.com/s?__biz=MzU4NDA1ODcwNA==&mid=2247489507&idx=1&sn=52cd0921b7e4156f5f9927f3d00b26a6&chksm=fd9ec5dccae94ccaa7000fbc0c6eea348ef6d1d7b700ab1cacf47144a50ee2d1fb39f95830bd&mpshare=1&scene=23&srcid=1221vjbd31QYyBbnvc4p2X1l&sharer_sharetime=1608514985512&sharer_shareid=4e2660e4c2de7c62b2fe690d1b52f383#rd
nextTick
nextTick方法主要是使用了宏任务和微任务定义了一个异步方法,
多次调用nextTick 会将方法存入队列中,
nextTick(cb)
callback.push(cb)
timerFunc()
1.尝试采用promise回调
2.尝试采用MutationObserver回调
3.尝试采用setmmediate回调
4.最终采用setTimeout回调
返回promise,支持promise写法
vue next-tick源码
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
export let isUsingMicroTask = false
//多次调用nextTick 传入的flushSchedulerQueue函数 存入这个数组里面
const callbacks = []
//异步锁
let pending = false
function flushCallbacks () {
pending = false
//拷贝一份数组,清空callbacks
const copies = callbacks.slice(0)
callbacks.length = 0
//指行callbacks数组
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
(就是又给这个数组传值,传的新值也会加入回调队列去执行)
如果 flushCallbacks 不做特殊处理,直接循环执行回调函数,
会导致里面nextTick 中的回调函数会进入回调队列
}
let timerFunc
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
timerFunc = () => {
//先采用的是微任务
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
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)) {
timerFunc = () => {
//让后才用的是这个异步函数
//该方法用来把一些需要长时间运行的操作放在一个回调函数里,
//在浏览器完成后面的其他语句后,就立刻执行这个回调函数,
setImmediate(flushCallbacks)
}
} else {
// 最后采用宏任务
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
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)
}
})
// 如果异步锁未锁上,锁上异步锁,
//调用异步函数,准备等同步函数执行完后,就开始执行回调函数队列
// nextTick源码中使用了一个异步锁的概念,即接收第一个回调函数时
// ,先关上锁,执行异步方法。
// 此时,浏览器处于等待执行完同步代码就执行异步代码的情况。
if (!pending) {
pending = true
timerFunc()
}
// $flow-disable-line
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
这里有两个问题需要注意:
如何保证只在接收第一个回调函数时执行异步方法?
nextTick源码中使用了一个异步锁的概念,即接收第一个回调函数时,
先关上锁,执行异步方法。此时,浏览器处于等待执行完同
步代码就执行异步代码的情况。
执行 flushCallbacks 函数时为什么需要备份回调函数队列?
执行的也是备份的回调函数队列?
因为,会出现这么一种情况:nextTick 的回调函数中还使用 nextTick。
如果 flushCallbacks 不做特殊处理,直接循环执行回调函数,
会导致里面nextTick 中的回调函数会进入回调队列。