Suggest an editImprove this articleRefine the answer for “Bottlenecks in JS code”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**The main bottlenecks are main thread blocking, excessive DOM work, memory leaks, inefficient loops and data structures, redundant network requests, poor rendering optimisation, a heavy bundle and the absence of profiling.** JavaScript is single threaded, so any long synchronous operation, a huge loop, `JSON.parse()` over megabytes or a heavy algorithm freezes the interface: the cure is chunking the work, Web Workers, debounce and throttle. DOM operations are the most expensive ones, so you batch changes, cache element references and avoid reading `offsetHeight` or `getComputedStyle` inside a loop, because that forces a reflow. Leaks come from uncleared timers, dangling event listeners, closures holding large objects and global variables. On large arrays, nested `O(n^2)` loops, `splice()` and `shift()` hurt, and a `Set` or a `Map` beats a linear search. Finally, every optimisation starts with a measurement in Chrome DevTools, not with a guess. ```javascript // An O(n) lookup inside a loop turns the task into O(n^2) const ids = new Set(activeIds); // O(1) per check const active = users.filter((u) => ids.has(u.id)); ``` **Key point:** the main enemies are long synchronous computation on the main thread, needless reflows and leaks; you find them by profiling, not by guessing.Shown above the full answer for quick recall.Answer (EN)Image**A bottleneck is the piece of code that caps the performance of the whole application, and in JavaScript there are eight typical ones: the main thread, the DOM, memory, loops, the network, rendering, the bundle and algorithms.** The ninth and most common is the absence of profiling, when people optimise at random. ## Theory ### TL;DR - JavaScript is single threaded: any heavy synchronous operation blocks the UI, rendering and event handling. - DOM operations are the most expensive; reading geometry inside a loop forces a reflow. - Memory leaks come from timers, event listeners, closures and global variables. - On large arrays the algorithm and the data structure decide everything: `Set` and `Map` instead of a linear search. - The network and bundle size often matter more than the speed of the code itself. - Animations belong on the GPU (`transform`, `opacity`, `requestAnimationFrame`). - Profile first, optimise second. ### Quick example ```javascript // Bad: reading geometry inside a loop makes the browser recalculate layout every time items.forEach((el) => { el.style.height = el.offsetHeight + 10 + 'px'; }); // Good: read everything first, then write everything const heights = items.map((el) => el.offsetHeight); items.forEach((el, i) => { el.style.height = heights[i] + 10 + 'px'; }); ``` ### Main thread blocking JavaScript is single threaded. Any heavy operation blocks the UI, rendering and event handling. **Examples:** - large loops (`for`, `while`, `forEach`) without `setTimeout` or `requestIdleCallback`; - parsing or processing large JSON payloads (`JSON.parse(hugeData)`); - computationally heavy algorithms (sorting, recursion, encryption); - synchronous requests (`XMLHttpRequest` without async); - large DOM changes inside a single frame. **How to fix it:** - split the work into chunks (`setTimeout`, `requestAnimationFrame`); - use **Web Workers** for background computation; - apply **debounce** and **throttle** to events. ### Excessive renders, DOM work and painting DOM operations are the most expensive thing you can do for performance. **Problems:** - inserting and removing elements too often; - style and layout recalculation on every change; - touching `offsetHeight`, `getComputedStyle` or `scrollTop` triggers a **reflow**; - loops that mutate the DOM inside the body. **How to fix it:** - use **DocumentFragment**, a **virtual DOM**, batch updates; - cache element references; - minimise `reflow` by switching a class for a whole group of elements at once; - in React: memoisation, `PureComponent`, `React.memo`, `useMemo`, `useCallback`. #### Weak rendering optimisation The UI stutters, animations lag, FPS drops. **Causes:** - changing `style` and `transform` too frequently; - heavy shadows, filters and `border-radius`; - not using the GPU (CSS animations without `transform: translateZ(0)`); - layout recalculation on every animation frame. **Solutions:** - move animations to the GPU; - combine DOM changes into a single frame; - use `will-change`, `transform`, `opacity`; - use `requestAnimationFrame` instead of timers for animation. ### Excessive memory usage Memory leaks make the tab heavier and heavier until the interface freezes. **Causes:** - uncleared timers (`setInterval`, `setTimeout`); - dangling event listeners that are never removed when the element goes away; - closures holding references to large objects; - global variables that are never cleaned up; - caches that are never evicted. **Solutions:** - clear timers (`clearInterval`, `clearTimeout`); - remove handlers (`removeEventListener`); - use `WeakMap` and `WeakSet`; - analyse through Chrome DevTools, the Memory tab. ### Inefficient loops, collections and algorithms This shows up especially on large arrays. **Typical mistakes:** - nested `O(n^2)` loops where they are not needed; - using `.map()`, `.filter()` and `.reduce()` on large arrays without optimisation; - creating new arrays or objects on every iteration; - frequent `Array.splice()` and `Array.shift()` calls, which are expensive operations. **What to do:** - use more suitable structures: `Set`, `Map`, `WeakMap`; - use iterators, generators and `for...of` instead of `forEach` on large volumes; - profile the hot spots with `performance.now()`. #### Suboptimal data structures An example: sorting an array of 100 000 elements with `sort()` and no comparator, or searching with `filter()` instead of a `Set`. **What to do:** - pick the structure for the task: `Set` for uniqueness, `Map` for fast lookups; - avoid a linear search where a hash lookup will do; - use binary search and cache computed results. ### Network, bundle and profiling Even fast JS code will not save you if the network is saturated. **Problems:** - repeated requests with no cache; - duplicate API calls on every render; - downloading enormous JS and CSS bundles; - no gzip or brotli compression; - no lazy loading. **How to fix it:** - caching (HTTP cache, IndexedDB, Service Worker, memoisation); - request batching; - React Query, SWR, the `Cache-Control` header; - code splitting and dynamic imports (`import()`). #### A heavy bundle The more JavaScript there is, the longer loading and parsing take. **Causes:** - pulling in unnecessary libraries; - duplicated dependencies; - not using tree-shaking; - inline JSON, large icons and images embedded in code. **Solutions:** - analyse the bundle (`webpack-bundle-analyzer`, `next build --analyze`); - tree-shaking, code splitting, dynamic `import()`; - use a CDN, HTTP/2 and ESM bundles; - minification (Terser, SWC). #### No profiling and no metrics The most common mistake of all is optimising blindly. **Solutions:** - Chrome DevTools (Performance, Memory, Coverage); - `console.time()`, `performance.mark()` and `performance.measure()`; - Lighthouse, Web Vitals; - Sentry Performance, New Relic, Datadog. A short summary: | Category | Example problem | How to fix it | | --- | --- | --- | | Thread blocking | A long loop | Split it up, use a Worker | | DOM | Too many reflows | Batch updates | | Memory | Leaks | WeakMap, cleanup | | Loops | O(n^2) | Optimise | | Network | Repeated requests | Cache | | Rendering | FPS < 60 | GPU animations | | Bundle | 2 MB of JS | Tree-shaking | | Algorithms | Wrong structure | Match it to the task | ### Common mistakes - **Optimising without measuring.** Without a profile in DevTools, effort goes into code that does not affect the result. - **Treating `async` as a cure for blocking.** `async/await` does not move computation to another thread: a heavy loop inside an `async` function still freezes the UI. A Web Worker is what you need. - **Interleaving DOM reads and writes.** The pattern "read geometry, write a style, read again" causes layout thrashing. Do all the reads first, then all the writes. - **Memoising everything.** `useMemo` and `useCallback` cost memory and comparisons themselves; on cheap computations they only add overhead. - **Forgetting to clean up.** A listener or `setInterval` created on mount and never removed on unmount is a leak by construction. - **Confusing bundle size with parse time.** Even a cached file has to be parsed and compiled, so the amount of code matters on repeat visits too. - **Micro-optimising loops instead of the algorithm.** Swapping `forEach` for `for` will not rescue an `O(n^2)` solution, moving to a `Map` will.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.