Recently, I found myself building a JavaScript-based animation within a front-end project I was working on. Here's a simplified version of the code:
const animate = () => {
// some sort of DOM update here
requestAnimationFrame(animate);
}
animate();
After a short while, I decided I wanted an FPS counter -- just something presentational that shows inside the browser. My first thought was to write a similar function, but for updating an FPS value shown on the page:
const ONE_SECOND = 1000;
let fps = 0;
const getFPS = startTime => {
window.requestAnimationFrame(() => {
const currentTime = performance.now();
if (currentTime - startTime < ONE_SECOND) {
fps++;
getFPS(startTime);
} else {
fpsDisplay.innerText = `${fps} FPS`;
fps = 0;
getFPS(currentTime);
}
});
};
getFPS(performance.now());
There is nothing seriously wrong with this approach. It updates properly and produces the correct value.
However, I quickly realised this solution was far from ideal: a second use of requestAnimationFrame
would make no sense if my project had animation-heavy code.
In fact, it was superfluous given that animate()
already included a requestAnimationFrame
call. I could achieve the same functionality by refactoring my getFPS()
function so that it shared the same call:
const ONE_SECOND = 1000;
let fps = 0;
const animate = () => {
// some sort of DOM update here
fps++;
requestAnimationFrame(animate);
}
const getFPS = () => {
setInterval(() => {
fpsDisplay.innerText = `${fps} FPS`;
fps = 0;
}, ONE_SECOND);
};
animate();
getFPS();
This is what's called colocating. The principle of colocation is simple: group like code with one another.
A good analogy for the above example is measuring the speed of a car. It would be an absurd waste of time and money to purchase a second car, match its speed with the first, and use a radar gun on the second. In comparison, the colocated code would be like installing a speedometer in the first car.
Colocating, in addition to decreasing code length and increasing readability, boasts significant computational savings. By refactoring the bulk of getFPS()
inside of animate()
, only a single instance of requestAnimationFrame()
was needed.
There are a myriad of benefits to colocation, including but not limited to easier readability and maintainability, better predictability, improved performance, and closer adherence to DRY principles.
Top comments (0)