Reputation: 1312
I want to write a system that automates max. 8192 different values consistently 40 times per second. The kind of automation, I want to do is e.g. letting some values form a sinus from 0 to 255, others ramp up from 50 to 80, others switch between 255 and 0 etc.
I need to know how long each execution takes and if the execution takes longer than allowed it should "drop a frame".
my first approach would be
// simplified code
const fps = 40
const executionStats : number[] = []
executionStats.length = fps * 10
const startFrame = new Date()
type SingleValue = {current: number, effectType: string, effectStarted: number}
const values : SingleValue[] = [...] // up to 8192 entries
// this is calculating the target value based on some lookups in other objects
// and based on when the effect started and in what tick we are in right now
const runSingleEffect = (value: SingleValue, tick: number) : number => {...}
const handler = async (tick: number) => {
values.forEach(value => {value.current = runSingleEffect(value, tick)})
}
setInterval(async () => {
const start = new Date()
await handler((start - startFrame) / fps);
const end = new Date()
executionStats.push(end - start)
executionStats.unshift()
}, 1000 / fps)
is there any better approach than using a simple setInterval
? (obviously I could use a ring buffer for the execution stats) but is there a better way to make sure that only one handler runs at a time? Is there maybe a library for that kind of stuff?
Upvotes: 0
Views: 290
Reputation: 8008
I would use setTimeout
with continuous resubscription. You would recalculate the next tick on every "loop" end.
function defer (ms) {
const frameBudgetMs = 1000 / fps;
setTimeout(async () => {
const start = Date.now();
try {
await handler((start - startFrame) / fps);
} finally {
const end = Date.now();
const ms = end - start;
executionStats.push(ms);
executionStats.unshift();
// a) In case we are over our frame, start next defer immediately.
let nextTickMs = Math.max(0, frameBudgetMs - ms);
defer(nextTickMs);
// b) In case we want to execute at specific frame interval, but just dropping the frames
let nextTickMs = frameBudget - ms % frameBudget;
defer(nextTickMs);
}
}, ms)
}
// start our loop
defer(0);
As you can see, you are completely flexible in resubscription scenarios, and no parallel runs are possible in such a "loop".
Upvotes: 1