欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS時間分片技術(shù)解決長任務(wù)導(dǎo)致的頁面卡頓

 更新時間:2022年07月26日 16:14:14   作者:愛喝葡萄汁的海豹  
旨在把一個運行時間比較長的任務(wù)分解成一塊一塊比較小的任務(wù),分塊去執(zhí)行,因為超過 50ms 的任務(wù)就會被認(rèn)為是 long task,用戶就能感知到渲染卡頓和交互的卡頓,所以我們可以縮短函數(shù)的連續(xù)執(zhí)行時間

起因

同事遇到一個動畫展示的問題,就是下面要執(zhí)行一個運算量很大的函數(shù),他要加載一個 loading,但他發(fā)現(xiàn)把 loading 的元素 display: block; 頁面中也不會立刻出現(xiàn) loading 動畫,出現(xiàn)動畫的時候是運算函數(shù)執(zhí)行完畢之后。

處理辦法

有兩種方法去處理這種耗時任務(wù),第一種就是 webWorker,但是一些 dom 的操作做不了,于是就想到了通過 generator 函數(shù)來解決,下面先簡單了解下事件循環(huán)。

事件循環(huán)

微任務(wù):

1. Promise.then

2. Object.observe

3. MutaionObserver

宏任務(wù):

1. script(整體代碼)

2. setTimeout

3. setInterval

4. I/O

5. postMessage

6. MessageChannel

瀏覽器渲染時機(jī)

除去特殊情況,頁面的渲染會在微任務(wù)隊列清空后,宏任務(wù)執(zhí)行前,所以我們可以讓推入主執(zhí)行棧的函數(shù)執(zhí)行到一定時間就去休眠,然后在渲染之后的宏任務(wù)里面叫醒他,這樣渲染或者用戶交互都不會卡頓了!

原始代碼

我們先模擬一個 js 長任務(wù)

代碼

// style
@keyframes move {
    from {
        left: 0;
    }
    to {
        left: 100%;
    }
}
.move {
    position: absolute;
    animation: move 5s linear infinite;
}
// dom
<div class="move">123123123</div>
// script
function fnc () {
    let i = 0
    const start = performance.now()
    while (performance.now() - start <= 5000) {
        i++
    }
    return i
}
setTimeout(() => {
    fnc()
}, 1000)

效果

如下圖,動畫運行 1s 的時候,js 函數(shù)開始運行,動畫會先停止渲染,然后等 js 主執(zhí)行??臻e之后動畫才繼續(xù)進(jìn)行。

函數(shù)改造

我們把原來的函數(shù)改造為 generator 函數(shù)

代碼

// generator 處理原來的函數(shù)
function * fnc_ () {
    let i = 0
    const start = performance.now()
    while (performance.now() - start <= 5000) {
        yield i++
    }
    return i
}
// 簡易時間分片
function timeSlice (fnc, cb = setTimeout) {
    if(fnc.constructor.name !== 'GeneratorFunction') return fnc()
    return async function (...args) {
        const fnc_ = fnc(...args)
        let data
        do {
            data = fnc_.next(await data?.value)
            // 每執(zhí)行一步就休眠,注冊一個宏任務(wù) setTimeout 來叫醒他
            await new Promise( resolve => cb(resolve))
        } while (!data.done)
        return data.value
    }
}
setTimeout(async () => {
    const fnc = timeSlice(fnc_)
    const start = performance.now()
    console.log('開始')
    const num = await fnc()
    console.log('結(jié)束', `${(performance.now() - start)/ 1000}s`)
    console.log(num)
}, 1000)

效果

動畫根本不受影響,fps 一直很穩(wěn)定,因為我們把耗時任務(wù)拆成很多個塊來執(zhí)行。

優(yōu)化時間分片

上面的時間分片函數(shù)每執(zhí)行一步,就會休眠,然后通過一個宏任務(wù)來喚醒他,但是這樣的執(zhí)行效率肯定是比較低的,我們再優(yōu)化一下執(zhí)行的效率,提升連續(xù)執(zhí)行時間。

代碼

// 精準(zhǔn)時間分片
function timeSlice_ (fnc, time = 25, cb = setTimeout) {
    if(fnc.constructor.name !== 'GeneratorFunction') return fnc()
    return function (...args) {
        const fnc_ = fnc(...args)
        let data
        return new Promise(async function go (resolve, reject) {
            try {
                const start = performance.now()
                do {
                    data = fnc_.next(await data?.value)
                } while (!data.done && performance.now() - start < time)
                if (data.done) return resolve(data.value)
                cb(() => go(resolve, reject))
            } catch(e) {
                reject(e)
            }
        })
    }
}
setTimeout(async () => {
    const fnc1 = timeSlice_(fnc_)
    let start = performance.now()
    console.log('開始')
    const num = await fnc1()
    console.log('結(jié)束', `${(performance.now() - start)/ 1000}s`)
    console.log(num)
}, 1000);

效果

我們把函數(shù)分成了較大的塊,這樣函數(shù)執(zhí)行的效率就會變高,fps 會稍微收到影響,但是在接受范圍內(nèi)。

對比優(yōu)化前后

我們對比一下優(yōu)化時間分片函數(shù)前后的效果

代碼

setTimeout(async () => {
    const fnc = timeSlice(fnc_)
    const fnc1 = timeSlice_(fnc_)
    let start = performance.now()
    console.log('開始')
    const a = await fnc()
    console.log('結(jié)束', `${(performance.now() - start)/ 1000}s`)
    console.log('開始')
    start = performance.now()
    const b = await fnc1()
    console.log('結(jié)束', `${(performance.now() - start)/ 1000}s`)
    console.log(a, b)
}, 1000);

效果

對比優(yōu)化后的時間分片函數(shù),是之前效率的 4452 倍,我們做的只是提升了函數(shù)連續(xù)執(zhí)行時間。

最后

generator 函數(shù)中 yield 的位置非常關(guān)鍵,需要放到耗時的地方,優(yōu)化后的時間分片函數(shù)也提供了 time 變量,你可以根據(jù)實際情況來改變你的 time 值。

以上就是JS時間分片技術(shù)解決長任務(wù)導(dǎo)致的頁面卡頓的詳細(xì)內(nèi)容,更多關(guān)于js時間分片長任務(wù)分解的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論