vue中nextTick的源码解读,分析js事件循环机制
2020-12-28 17:31
标签:define 同步 队列的实现 就会 执行 callback fun 操作 ant vue中nextTick的源码解读,分析js事件循环机制 标签:define 同步 队列的实现 就会 执行 callback fun 操作 ant 原文地址:https://www.cnblogs.com/aloneMing/p/14168749.html代码位置
nextTick
的实现在src/core/util/next-tick.js
中。主要代码块
nextTick
异步回调的途径。// 首先是看当前环境支不支持Promise,如果支持Promise就使用Promise,添加了一个微任务
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]‘
)) { // 如果不支持Promise,就看支持不支持MutationObserver,相当于添加了一个微任务
// e.g. PhantomJS, iOS7, Android 4.4
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)) { // 再看支不支持setImmediate,添加了一个宏任务,在下一轮的事件队列中调用执行
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else { // 都不支持就使用setTimeout来代替,添加了一个宏任务,在下一轮的事件队列中调用执行
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}
nextTick
的实现。export function nextTick (cb?: Function, ctx?: Object) {
let _resolve;
callbacks.push(() => {
// 判断nextTick中有没有传入回调,传入了回调就把回调放到callbacks的回调队列中
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, ‘nextTick‘)
}
} else if (_resolve) { // 没有传入回调,就使用默认的回调,前提是默认的回调存在
_resolve(ctx)
}
})
if (!pending) { // 当回调队列没有执行时,才会去触发新的执行操作
pending = true // 表示异步队列正在执行
timerFunc() // 调用异步执行的方法,就是上面那一步的操作
}
// $flow-disable-line
if (!cb && typeof Promise !== ‘undefined‘) { // 当没有传入回调,并且Promise可以使用时,把默认的回调变成Promise的resolve操作
return new Promise(resolve => {
_resolve = resolve
})
}
}
flushCallbacks
清空队列的实现function flushCallbacks () {
pending = false // 清空队列之后,当前的状态又是pending等待中
const copies = callbacks.slice(0) // 将回调队列拷贝一份出来
callbacks.length = 0 // 清空回调队列
for (let i = 0; i
原理解读
nextTick
的本质是利用了JS
的事件队列的机制,动态的添加微任务或者宏任务,使得在nextTick
中的回调可以异步调用,通常是在页面的data
更新过之后。事件队列
事件队列中的常见宏任务和微任务
// 当只有三个宏任务的时候,结果是123/132/213,都有可能
setTimeout(() => {
console.log(1)
}, 0)
setImmediate(() => {
console.log(2)
})
setTimeout(() => {
console.log(3)
}, 0)
// 当主线程存在其它任务时,基本上就是setImmediate要比setTimeout(fn, 0)要执行的晚一点,所以尝试了很多次还是48756132
setTimeout(() => {
console.log(1) // 宏任务第一步
}, 0)
setImmediate(() => {
console.log(2) // 宏任务第三步
})
setTimeout(() => {
console.log(3) // 宏任务第二步
}, 0)
new Promise((resolve) => {
console.log(4); // 主线程第一步
resolve();
}).then(() => {
console.log(5); // 微任务队列第二步
}).finally(() => {
console.log(6) // 微任务队列第三步
});
process.nextTick(() => {
console.log(7) // 主线程末尾执行,微任务第一步
});
console.log(8); // 主线程第二步
// 由于必须要先把此次事件队列中的微任务执行完毕后才可以去执行宏任务
// 又因为在微任务中定义的微任务依然需要在此次事件循环中执行完
// 微任务或宏任务中定义的宏任务会放到下一次的事件循环中去执行
// 所以最终结果就是:1 > 4 > 3 > 5 > 7 > 6 > 2 > 8
process.nextTick(() => {
console.log(1); // 微任务第一步
setTimeout(() => {
console.log(2); // 下一轮的宏任务第一步
}, 0)
Promise.resolve(3).then((data) => {
console.log(data) // 微任务第三步
});
})
Promise.resolve(4).then(data => {
console.log(data); // 微任务第二步
})
setTimeout(() => {
console.log(5); // 宏任务第一步
Promise.resolve(6).then(data => {
console.log(data); // 下一轮的微任务第二步
})
process.nextTick(() => {
console.log(7); // 下一轮的微任务第一步
})
setTimeout(() => {
console.log(8); // 下一轮的宏任务第二步
})
})
上一篇:网页打印出现空白页问题解决
下一篇:webrtc-audioproc
文章标题:vue中nextTick的源码解读,分析js事件循环机制
文章链接:http://soscw.com/index.php/essay/38879.html