小趴

遞增(增量)輪詢

我們平常開發中,經常用到,輪詢。但是有些場景下, 對某些輪詢有一些額外的要求。 例如我現在公司在做的項目中,後臺有隊列任務,如果隊列任務少,或者空閒的話,那麼這個任務很快就能夠完成, 否則,就需要排隊。

那麼一開始,前臺發送了一個任務執行請求的時候,接着前臺這邊會開始輪詢這個任務的執行狀態,完成了沒有。設定的輪詢時間是5秒。

但是前臺的反饋就會有一個問題,哪怕這個任務在1秒鐘就完成了。 前臺也不會變更狀態,只有等5秒後的下一次請求,然後再變更頁面狀態。

我們這個業務暫時沒有用到 socket, 所以實時性沒有那麼高。

那有沒有別的思路呢?

如果我直接將間隔時間調整爲很短,這樣頁面確實可以較快響應任務的狀態變更。 但是如果任務很多的時候,後臺壓力會很大。

是否可以假設,前臺每次請求執行一個任務後,我先是間隔很短的去輪詢,如果後臺隊列比較空閒,那麼很快就能更新狀態了, 然後我慢慢的遞增間隔的輪詢時間,直到一個設定的最大值。

假設,我起始最少間隔是200毫秒,然後遞增步長是100毫秒,最大間隔是1500毫秒。

現在如果有一個任務請求執行需要1秒,那麼我會分別以以下間隔發送請求:

200ms,200ms+100ms,200ms+100ms+100ms,200ms+100ms+100ms+100ms

四次請求的總時長爲 1400ms, 所以我在第四次請求就能夠更新頁面狀態了,

而如果某個任務請求執行需要10分鐘,那麼前臺會一次累加請求的間隔時間,200ms,300ms,400ms,500ms,.....直到1500ms, 此時如果狀態還沒有完成,那麼後面就每隔1500ms去查詢一次。

實現

根據以上思路,我們有如下實現:

/**
 * 
 * @param min 起始間隔
 * @param max 最大延遲
 * @param step 步進延遲
 * @param cb 回調函數(執行函數)
 * @param immediate 是否立即執行
 * @param _count (私有屬性,也可以傳遞,傳遞後首次延遲將會是 min + step * _count)
 */
const incrementWalk = function (min: number, max: number, step: number, cb: () => void, immediate = false, _count = 0) {
    immediate && cb();
    // console.time()
    const nextDelay = min + step * _count
    setTimeout(() => {
        // console.timeEnd()
        incrementWalk(min, max, step, cb, immediate, _count + 1)
    }, nextDelay > max ? max : nextDelay)
}

const callback = () => {
    console.log("trigger")
}

incrementWalk(100, 5000, 300, callback, true)

遞增(增量)輪詢,解決了什麼問題?

解決了在某個任務耗時不確定,又希望儘快刷新頁面狀態,同時又要兼顧服務器壓力問題。

階梯輪詢

解決了上一個問題後,我在想, 有沒有一種情況, 可以指定前幾次請求延時分別不同。 例如, 前10次間隔500ms, 從第10次開始,到第20次結束, 中間每次請求間隔1000ms, 類似這種, 然後按照某個固定間隔輪詢?

所以有了如下實現:

/**
 * 
 * @param tieredArray 二維數組,其數據組成爲 Array<[delay時間,次數]>
 * @param constantDelay 固定間隔時間,也就是指定間隔走完了,後面的固定間隔延時
 * @param cb 回調函數(待執行函數)
 */
async function tieredWalk(tieredArray: number[][], constantDelay: number, cb: () => void) {
    for (let i = 0; i < tieredArray.length; i++) {
        const [delay, times] = tieredArray[i]
        await new Promise<void>((resolve, reject) => {
            function innerWalker(times: number) {
                setTimeout(() => {
                    try {
                        cb()
                    } catch (err) {
                        reject(err)
                    }
                    times--
                    if (times > 0) {
                        innerWalker(times)
                    } else {
                        resolve()
                    }
                }, delay)
            }
            innerWalker(times)
        })

    }

    outerWalker()
    function outerWalker() {
        cb()
        setTimeout(() => {
            console.log("outer")
            outerWalker()
        }, constantDelay)
    }

}

let a = 0
const cb = function () {
    a++
    if(a > 5) return;
    console.log('trigger')
}
tieredWalk([[1000, 3], [200, 2], [2000, 2], [400, 3]], 1000, cb)

這段代碼,


呆逼解決方案

/**
 * 
 * @param tieredArray 二維數組,其數據組成爲 Array<[delay時間,次數]>
 * @param constantDelay 固定間隔時間,也就是指定間隔走完了,後面的固定間隔延時
 * @param cb 回調函數(待執行函數)
 */
async function tieredWalk(tieredArray: number[][], constantDelay: number, cb: () => void) {
    let clearFlag = false
    for (let i = 0; i < tieredArray.length; i++) {
        const [delay, times] = tieredArray[i]
        await new Promise<void>((resolve, reject) => {
            function innerWalker(times: number) {
                setTimeout(() => {
                    try {
                        cb()
                    } catch (err) {
                        reject(err)
                    }
                    times--
                    if (times > 0) {
                        innerWalker(times)
                    } else {
                        resolve()
                    }
                }, delay)
            }
            innerWalker(times)
        })

    }

    outerWalker()
    function outerWalker() {
        if(clearFlag) return;
        cb()
        setTimeout(() => {
            console.log("outer")
            outerWalker()
        }, constantDelay)
    }

    return function(){clearFlag=true}

}


(async function(){
    let a = 0
    const cb = function () {
        a++
        if(a > 5) return;
        console.log('trigger')
    }
    const clear = await tieredWalk([[1000, 3], [200, 2], [2000, 2], [400, 3]], 1000, cb)
    if(a>5) clear()

})()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章