nextTick:
nextTick主要是使用了宏任務 (macrotask) 和微任務 (microtask) ,定義了一個非同步方法,多次呼叫 nextTick 會將方法存入callback佇列中,通過這個非同步方法清空當前佇列
macrotask:
setTimeout, setInterval, setImmediate, I/O, UI rendering
microtask:
process.nextTick, Promise, MutationObserver
任務佇列中,在每一次事件迴圈中,macrotask只會提取一個執行,而microtask會一直提取,直到microsoft佇列為空為止,主執行緒執行完成該任務後又會檢查microtasks佇列並完成裡面的所有任務後再執行macrotask
nextTick(callback): 全域性方法,當資料發生變化,更新後執行回撥
$nextTick(callback): 實體方法,自動把context引數繫結為呼叫它的範例,當dom發生變化,更新後執行的回撥,一般會使用this. $nextTick
nextTick
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)
}
})
// 如果當前沒有在執行,就會執行timeFunc
if (!pending) {
//標記正在執行
pending = true
// 多次執行nextTick只會執行一次,timerFunc就是一個非同步方法
timerFunc()
}
}
timeFunc
// 判斷是否原生支援promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
const p = Promise.resolve()
// flushCallbacks就包裹了一個promise
timerFunc = () => {
// 如果支援則非同步的去執行flushCallbacks
p.then(flushCallbacks)
if (isIOS) setTimeout(noop)
}
// 標記微任務
isUsingMicroTask = true
// 判斷是否原生支援MutationObserver
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// 也是一個微任務
let counter = 1
// new了一個MutationObserver類
const observer = new MutationObserver(flushCallbacks)
// 建立了一個文位元組點
const textNode = document.createTextNode(String(counter))
// 原生api,幫我們監聽一個節點
// 當資料發生變化了就會非同步執行flushCallbacks方法
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
// 資料更新
textNode.data = String(counter)
}
// 標記微任務
isUsingMicroTask = true
// 判斷是否原生支援setImmediate
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// setImmediate原生方法,預設ie下有,高版本的谷歌也支援
timerFunc = () => {
// 直接執行
setImmediate(flushCallbacks)
}
} else {
// 如果以上都不支援則採用setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
flushCallbacks
// 回撥函數佇列
const callbacks = []
// 空閒狀態準備執行
let pending = false
// 多個nextTick中傳遞的回撥函數依次執行
function flushCallbacks () {
pending = false
// 拷貝一份禁止套娃
const copies = callbacks.slice(0)
// 清空佇列
callbacks.length = 0
// cb執行過程中可能又會往callbacks中加入內容
// 遍歷完拷貝的佇列,新任務在下一輪存入callbacks執行
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}