vue如何sleep_浅谈Tick的实现⽅法
这是⼀篇继event loop和MicroTask 后的Tick API实现的源码解析。
预热,写⼀个sleep函数
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms)
}
async function oneTick (ms) {
console.log('start')
await sleep(ms)
console.log('end')
}
oneTick(3000)
解释下sleep函数
async 函数进⾏await PromiseFn()时函数执⾏是暂停的,我们也知道现在这个PromiseFn是在microTask内执⾏。当microTask没执⾏完毕时,后⾯的macroTask是不会执⾏的,我们也就通过microTask在event loop的特性实现了⼀个sleep函数,阻⽌了console.log的执⾏
流程
1执⾏console.log('start')
2执⾏await 执⾏暂停,等待await函数后的PromiseFn在microTask执⾏完毕
3在sleep函数内,延迟ms返回
4返回resolve后执⾏console.log('end')
nextTick API
vue中nextTick的使⽤⽅法
//
})
了解⽤法后看⼀下源码
const nextTick = (function () {
const callbacks = []
let pending = false
let timerFunc // 定时函数
function nextTickHandler () {
pending = false
callbacks.length = 0 // 清空
for (let i = 0; i < copies.length; i++) {
pendingcopies[i]() // 逐个执⾏
}
}
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = solve()
var logError = err => { (err) }
timerFunc = () => {
p.then(nextTickHandler).catch(logError) // 重点
}
} else if ('!isIE MutationObserver') {
var counter = 1
var observer = new MutationObserver(nextTickHandler) // 重点var textNode = ateTextNode(string(conter)) observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else {
timerFunc = () => {
setTimeout(nextTickHandler, 0) // 重点
}
}
return function queueNextTick (cb, ctx) { // api的使⽤⽅式
let _resolve
callbacks.push(() => {
if (cb) {
try {
err
}
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise((resolve, reject) => {
_resolve =resolve
})
}
}
})() // ⾃执⾏函数
⼤致看⼀下源码可以了解到nextTick api是⼀个⾃执⾏函数
既然是⾃执⾏函数,直接看它的return类型,return function queueNextTick (cb, ctx) {...} return function queueNextTick (cb, ctx) { // api的使⽤⽅式
let _resolve
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
err
}
} else if (_resolve) {
_resolve(ctx)
}
})
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise((resolve, reject) => {
_resolve =resolve
})
}
}
只关注主流程queueNextTick函数把我们传⼊的() => { // } 推⼊了callbacks内
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = solve()
var logError = err => { (err) }
timerFunc = () => {
p.then(nextTickHandler).catch(logError) // 重点
}
} else if ('!isIE MutationObserver') {
var counter = 1
var observer = new MutationObserver(nextTickHandler) // 重点
var textNode = ateTextNode(string(conter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else {
timerFunc = () => {
setTimeout(nextTickHandler, 0) // 重点
}
}
这⼀段我们可以看到标注的三个点表明在不同浏览器环境下使⽤Promise, MutationObserver或setTimeout(fn, 0) 来执⾏nextTickHandler
pending = false
const copies = callbacks.slice(0) // 复制
callbacks.length = 0 // 清空
for (let i = 0; i < copies.length; i++) {
copies[i]() // 逐个执⾏
}
}
nextTickHandler就是把我们之前放⼊callbacks的 () => { // } 在当前tasks内执⾏。
写⼀个简单的nextTick
源码可能⽐较绕,我们⾃⼰写⼀段简单的nextTick
const simpleNextTick = (function () {
let callbacks = []
let timerFunc
return function queueNextTick (cb) {
callbacks.push(() => { // 给callbacks 推⼊cb()
cb()
})
timerFunc = () => {
solve().then(() => {
const fn = callbacks.shift()
fn()
})
}
timerFunc() // 执⾏timerFunc,返回到是⼀个Promise
}
})()
simpleNextTick(() => {
setTimeout(console.log, 3000, 'nextTick')
})
我们可以从这⾥看出nextTick的原理就是返回出⼀个Promise,⽽我们todo的代码在这个Promise中执⾏,现在我们还可以继续简化const simpleNextTick = (function () {
return function queueNextTick (cb) {

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。