概念:
nextTick:
nextTick主要是使用了宏任務 (macrotask) 和微任務 (microtask) ,定義了一個異步方法,多次呼叫 nextTick 會將方法存入callback佇列中,通過這個異步方法清空當前佇列
macrotask:
setTimeout, setInterval, setImmediate, I/O, UI rendering
microtask:
process.nextTick, Promise, MutationObserver
順序:
任務佇列中,在每一次事件回圈中,macrotask只會提取一個執行,而microtask會一直提取,直到microsoft佇列為空為止,主執行緒執行完成該任務后又會檢查microtasks佇列并完成里面的所有任務后再執行macrotask
場景:
- 在資料變化后要執行的某個操作,并隨著操作需要改變dom更新的時候
- 在 created 或者 mounted 階段,需要操作后更新dom時
- 如果先執行了nextTick再更新資料會無效
nextTick和$nextTick:
nextTick(callback): 全域方法,當資料發生變化,更新后執行回呼
$nextTick(callback): 實體方法,自動把context引數系結為呼叫它的實體,當dom發生變化,更新后執行的回呼,一般會使用this. $nextTick
原理:
- 呼叫nextTick并傳入兩個引數:回呼函式cb和回呼函式的執行背景關系ctx
- 判斷是否有回呼函式
- 有就存入佇列
- 沒有就回傳promise
- 判斷是否在執行回呼函式
- 如果沒有則執行timeFunc異步方法,多次執行nextTick只會執行一次timerFunc
- timeFunc中選擇一個異步方法:
(1)先嘗試promise回呼,進行異步執行flushCallbacks
(2)若不支持則繼續嘗試MutationObserver回呼,會創建一個文本節點進行監聽
(3)若不支持則繼續嘗試setImmediate回呼,在setImmediate下執行flushCallbacks
(4)都不支持則使用setTimeout(flushCallbacks, 0) - 執行flushCallbacks方法
原始碼:
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]()
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/158486.html
標籤:其他
下一篇:【密碼學原理】RSA演算法
