Is Your Site Slow? Time to Profile JavaScript
\nHave you ever felt that pain: the site loads, animations are smooth, but as soon as the user starts actively interacting with the page — everything grinds to a halt. Buttons don't respond, scrolling stutters, and the console is silent. Sound familiar? Chances are, the problem is JavaScript performance. This language is the soul of the modern web, but without proper optimization, it can turn your masterpiece into a sluggish monster.
\nIn this article, we won't bore you with dull lectures. We'll take real code, break it down piece by piece using profiling, and show you how to squeeze the maximum out of every loop. Ready to turn your site into a Formula 1 race car? Let's go!
\n\nWhy JavaScript Slows Down: The Root of Evil
\nBefore grabbing tools, let's figure out what exactly steals performance. Most often, three things are to blame:
\n- \n
- Main thread blocking — JavaScript runs in a single thread (Event Loop). If you run a heavy synchronous operation, the interface will freeze until it completes. \n
- Memory leaks — forgotten event handlers, closure references, and global variables. Memory grows, the site slows down, and then crashes. \n
- Inefficient algorithms — nested loops over huge arrays, frequent DOM manipulations, suboptimal data structures. \n
But enough theory. Let's see what this looks like in practice.
\n\nProfiling Tools: Your Lie Detector
\nChrome DevTools — The Swiss Army Knife of Profiling
\nOpen any page, press F12, and go to the Performance tab. Click the record button, perform an action on the page (e.g., click a button), and stop the recording. You'll see a timeline where every second is broken down by frames. Red bars are your enemies. They mean the browser failed to render a frame within 16.6 ms (60 FPS).
\n// Example: a simple but problematic function\nfunction heavyCalculation() {\n let result = 0;\n for (let i = 0; i < 100000000; i++) {\n result += Math.sqrt(i) * Math.sin(i);\n }\n return result;\n}\n\ndocument.getElementById('btn').addEventListener('click', () => {\n const start = performance.now();\n heavyCalculation(); // Interface will freeze for 2-3 seconds!\n console.log('Time:', performance.now() - start);\n});\n\nRun profiling and click the button. You'll see the main thread is busy with the heavyCalculation call for a whole second. That's the bottleneck.
Memory — Hunting for Leaks
\nGo to the Memory tab and take a heap snapshot. Then perform an action on the page several times and take a second snapshot. Compare them. If the number of objects of the same type keeps growing — you have a leak.
\n// Classic leak: a forgotten handler\nfunction createButton() {\n const btn = document.createElement('button');\n btn.textContent = 'Click me';\n document.body.appendChild(btn);\n \n // The handler holds a reference to btn, even if btn is removed\n btn.addEventListener('click', function onClick() {\n console.log('Clicked');\n });\n \n // Remove the button, but the handler remains in memory\n document.body.removeChild(btn);\n}\n\n\nOptimization Techniques: From Theory to Practice
\n1. Offload the Main Thread with async/await
\nIf you have a heavy task, don't block the thread. Use Web Workers or break the task into microtasks. But the simplest way is setTimeout with zero delay or requestIdleCallback.
// Bad: synchronous loading\nfunction loadBigData() {\n const data = [];\n for (let i = 0; i < 1000000; i++) {\n data.push({ id: i, value: Math.random() });\n }\n render(data);