Efficient timer implementations for Bloxd.io scripting by FrostyCaveman.

Optimized for lower amount of active timers

Starts getting slow after around 300 active timers.

Minified:

TTimers={};{const k=c=>api.broadcastMessage(c,{color:"#ff9d87"}),s=TTimers;let m=1,l=0,f=0,T=0,o=1/0,a;setTickTimeout=(c,i=0)=>{const e=m++,t=l+i;if(s[e]=[c,t,T,0],T?s[T][3]=e:f=e,T=e,t<o&&(o=t),a=!0,!i){try{c()}catch(r){k("TTI: "+r)}clearTickTimeout(e)}return e},clearTickInterval=clearTickTimeout=c=>{const i=s[c];if(i){const e=i[2],t=i[3];e?s[e][3]=t:f=t,t?s[t][2]=e:T=e,delete s[c]}},setTickInterval=(c,i)=>{const e=setTickTimeout(c,i||=1);return s[e][4]=i,e},tick=(c,i)=>{if(i||++l>=o){a=!1;let e=1/0,t=f;for(;t;){const r=s[t];if(r[1]<=l){try{r[0]()}catch(n){k("TT: "+n)}if(r[4]){const n=l+r[4];r[1]=n,n<e&&(e=n)}else clearTickTimeout(t)}else r[1]<e&&(e=r[1]);t=r[3]}o=e,a&&tick(1,!0)}}}

Source:

TTimers = {} // id to [fn, targetTick, prev, next, repeatDelay?]
{
    const error = msg => api.broadcastMessage(msg, { color: "#ff9d87" })
    const timers = TTimers
    let nextId = 1
    let tickCount = 0
    let head = 0
    let tail = 0
    let minTargetTick = Infinity
    let timersAddedMidProc

    setTickTimeout = (fn, delay = 0) => {
        const id = nextId++
        const targetTick = tickCount + delay
        timers[id] = [fn, targetTick, tail, 0]

        if (tail)
            timers[tail][3] = id
        else
            head = id

        tail = id

        if (targetTick < minTargetTick)
            minTargetTick = targetTick

        timersAddedMidProc = true

        if (!delay) {
            try { fn() }
            catch (e) { error("TTI: " + e) }
            clearTickTimeout(id)
        }

        return id
    }

    clearTickInterval = clearTickTimeout = id => {
        const timer = timers[id]
        if (timer) {
            const prev = timer[2]
            const next = timer[3]

            if (prev)
                timers[prev][3] = next
            else
                head = next

            if (next)
                timers[next][2] = prev
            else
                tail = prev

            delete timers[id]
        }
    }

    setTickInterval = (fn, delay) => {
        const id = setTickTimeout(fn, delay ||= 1)
        timers[id][4] = delay
        return id
    }

    tick = (fifty, noTick) => {
        if (noTick || ++tickCount >= minTargetTick) {
            timersAddedMidProc = false
            let newMinTick = Infinity

            let id = head
            while (id) {
                const timer = timers[id]
                if (timer[1] <= tickCount) {
                    try { timer[0]() }
                    catch (e) { error("TT: " + e) }
                    if (timer[4]) {
                        const newTargetTick = tickCount + timer[4]
                        timer[1] = newTargetTick
                        if (newTargetTick < newMinTick)
                            newMinTick = newTargetTick
                    } else {
                        clearTickTimeout(id)
                    }
                } else if (timer[1] < newMinTick) {
                    newMinTick = timer[1]
                }
                id = timer[3]
            }
            minTargetTick = newMinTick

            if (timersAddedMidProc)
                tick(1, true)
        }
    }
}

Optimized for high amount of active timers

This is a little slower for lower amount of active timers while still fast.

Minified:

TTimers={};{const l=e=>api.broadcastMessage(e,{color:"#ff9d87"}),i=TTimers,s={};let d=1,n=0,o=0,T;setTickTimeout=(e,c=0)=>{const t=d++,r=n+(c+!c),a=s[r]??=[];if(i[t]=[e,r],a[a.length]=t,!c){try{e()}catch(k){l("TTI: "+k)}clearTickTimeout(t)}return t},setTickInterval=(e,c)=>{const t=setTickTimeout(e,c||=1);return i[t][2]=c,t},clearTickInterval=clearTickTimeout=e=>delete i[e],tick=()=>{const e=s[n+=!T];if(T=!0,e)for(;o<e.length;++o){const c=e[o],t=i[c];if(t){try{t[0]()}catch(r){l("TT: "+r)}if(t[2]){const r=s[t[1]=n+t[2]]??=[];r[r.length]=c}else delete i[c]}}T=!1,delete s[n],o=0}}

Source:

TTimers = {} // id to [fn, targetTick, repeatDelay?]
{
    const error = msg => api.broadcastMessage(msg, { color: "#ff9d87" })
    const timers = TTimers
    const tickToIds = {}
    let nextId = 1
    let tickCount = 0
    let processingTimerIndex = 0
    let wasInterrupted

    setTickTimeout = (fn, delay = 0) => {
        const id = nextId++
        const targetTick = tickCount + (delay + !delay) // || 1
        const tArr = tickToIds[targetTick] ??= []
        timers[id] = [fn, targetTick]
        tArr[tArr.length] = id
        if (!delay) {
            try { fn() }
            catch (e) { error("TTI: " + e) }
            clearTickTimeout(id)
        }
        return id
    }

    setTickInterval = (fn, delay) => {
        const id = setTickTimeout(fn, delay ||= 1)
        timers[id][2] = delay
        return id
    }

    clearTickInterval = clearTickTimeout = id => delete timers[id]

    tick = () => {
        const ids = tickToIds[tickCount += !wasInterrupted] // +1 if wasn't interrupted
        wasInterrupted = true
        if (ids) for (; processingTimerIndex < ids.length; ++processingTimerIndex) {
            const id = ids[processingTimerIndex]
            const timer = timers[id]
            if (!timer) continue

            try { timer[0]() }
            catch (e) { error("TT: " + e) }

            if (timer[2]) {
                const tArr = tickToIds[
                    timer[1] = tickCount + timer[2]
                ] ??= []
                tArr[tArr.length] = id
            } else {
                delete timers[id]
            }
        }
        wasInterrupted = false
        delete tickToIds[tickCount]
        processingTimerIndex = 0
    }
}



Old, wrong and slow version. Do not use.

var tickTimers
function setTickTimeout(cb, delay = 1, _repeat) {
    const timer = [cb, api.now() + delay * 50, delay, _repeat]
    return (tickTimers ??= new Set()).add(timer), timer
}
function setTickInterval(cb, delay) { return setTickTimeout(cb, delay, true) }
function clearTickTimeout(timer) { tickTimers?.delete(timer) }
function clearTickInterval(timer) { clearTickTimeout(timer) }

tick = () => {
    if (tickTimers?.size) {
        const now = api.now()
        for (const timer of tickTimers) {
            const [cb, time, delay, repeat] = timer
            if (now >= time) {
                try { cb() }
                catch (err) { api.broadcastMessage("Error in tick timer: " + err, { color: "#ff9d87" }) }
                if (repeat) timer[1] = now + delay * 50
                else tickTimers.delete(timer)
            }
        }
    }
}
Edit

Pub: 26 Apr 2025 14:55 UTC

Edit: 30 Oct 2025 15:39 UTC

Views: 239