异步事件优先级:Promise > MutationObserver > setImmediate > setTimeout
// 是否使用了微任务来处理异步更新export let isUsingMicroTask = false// 更新队列const callbacks = []// 是否正在等待开启更新中,当 pending 为 false 时,意味着异步更新队列已经为空,并且下一个事件循环没有更新任务let pending = false// 更新函数// 一旦执行更新,则直接清空队列,并取其浅拷贝进行更新function flushCallbacks () {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()}}// 异步函数let timerFunc// 1. 如果当前环境有 Promise,则使用 Promise 当异步更新函数// 2. 如果不是 IE 且支持 MutationObserver,则使用 MutationObserver 的回调函数作为异步更新函数// 3. 如果支持 setImmediate,则使用 setImmediate 作为异步更新函数// 4. 最下策,使用 setTimeout 作为一部更新函数if (typeof Promise !== 'undefined' && isNative(Promise)) {const p = Promise.resolve()timerFunc = () => {p.then(flushCallbacks)// 再 IOS webview,会出现一些奇怪的情况,级微任务队列未刷新,解决这个问题的方法就是注册一个空的宏任务if (isIOS) setTimeout(noop)}isUsingMicroTask = true} else if (!isIE && typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) ||MutationObserver.toString() === '[object MutationObserverConstructor]')) {let counter = 1const observer = new MutationObserver(flushCallbacks)const textNode = document.createTextNode(String(counter))observer.observe(textNode, {characterData: true})timerFunc = () => {counter = (counter + 1) % 2textNode.data = String(counter)}isUsingMicroTask = true} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {timerFunc = () => {setImmediate(flushCallbacks)}} else {// Fallback to setTimeout.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)}})// 如果当前是第一个入队的,则开启异步更新,并将 pending 置否if (!pending) {pending = truetimerFunc()}// 如果没有 cb,并且当前环境支持 Promise,则返回一个 Promise,resolve(ctx)if (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}}
