debounce.mjs

import isfun from './isfun.mjs'
import ispint from './ispint.mjs'
import cint from './cint.mjs'


/**
 * 函數去除抖動
 *
 * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/debounce.test.mjs Github}
 * @memberOf wsemi
 * @param {Integer} [ms=300] 輸入未有調用的時間區間,為正整數,預設300ms
 * @example
 *
 * async function topAsync() {
 *
 *     function test1() {
 *         return new Promise((resolve, reject) => {
 *             let ms = []
 *
 *             let dbc = debounce(300)
 *
 *             let i = 0
 *             function core(name) {
 *                 i++
 *                 ms.push({ name, i })
 *                 console.log({ name, i })
 *             }
 *
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('A')
 *                 })
 *             }, 100)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('B')
 *                 })
 *             }, 200)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('C')
 *                 })
 *             }, 250)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('D')
 *                 })
 *             }, 350)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('E')
 *                 })
 *             }, 400)
 *             setTimeout(function() {
 *                 resolve(ms)
 *             }, 800)
 *         })
 *     }
 *     console.log('test1')
 *     let r1 = await test1()
 *     console.log(JSON.stringify(r1))
 *     // test1
 *     // { name: 'E', i: 1 }
 *     // [{"name":"E","i":1}]
 *
 *     function test2() {
 *         return new Promise((resolve, reject) => {
 *             let ms = []
 *
 *             let dbc = debounce(300)
 *
 *             let i = 0
 *             function core(name) {
 *                 i++
 *                 ms.push({ name, i })
 *                 console.log({ name, i })
 *             }
 *
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('A')
 *                 })
 *             }, 50)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('B')
 *                 })
 *             }, 100)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('C')
 *                 })
 *             }, 150)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('D')
 *                 })
 *             }, 500)
 *             setTimeout(function() {
 *                 dbc(() => {
 *                     core('E')
 *                 })
 *             }, 550)
 *             setTimeout(function() {
 *                 resolve(ms)
 *             }, 1400)
 *         })
 *     }
 *     console.log('test2')
 *     let r2 = await test2()
 *     console.log(JSON.stringify(r2))
 *     // test2
 *     // { name: 'C', i: 1 }
 *     // { name: 'E', i: 2 }
 *     // [{"name":"C","i":1},{"name":"E","i":2}]
 *
 * }
 * topAsync().catch(() => {})
 *
 */
function debounce(ms = 300) {

    function ClsDebounce(ms) {
        let q = [] //queue
        let t = null //timer
        let tLast = null

        //ms
        if (!ispint(ms)) {
            ms = 300
        }
        ms = cint(ms)

        function detect() {
            if (t !== null) {
                return
            }
            t = setInterval(() => {
            //console.log('q', q)

                let tDiff = Date.now() - tLast
                if (tDiff > ms) { //超過指定延時則呼叫指定func

                    //取最後的任務與清空佇列
                    let m = q.pop()
                    q = []

                    //執行最後的任務
                    m.func(...m.input)

                }

                //clear
                if (q.length === 0) {
                    clearInterval(t)
                    t = null
                }

            }, 10) //10ms偵測, 啟動後跑timer, 無佇列則會停止減耗
        }

        function run(func, ...input) {

            //check
            if (!isfun(func)) {
                console.log('func is not a function')
                return
            }

            //save
            tLast = Date.now()
            q.push({ func, input })

            //detect
            detect()

        }

        return run
    }

    return new ClsDebounce(ms)
}


export default debounce