阿里云主机折上折
  • 微信号
Current Site:Index > Strategies to avoid Long Tasks

Strategies to avoid Long Tasks

Author:Chuan Chen 阅读数:30127人阅读 分类: 性能优化

Understanding the Definition of Long Tasks

Long Tasks refer to tasks executed on the main thread that exceed 50 milliseconds in duration. Browsers flag such tasks as potential issues that may block user interactions. Frequent occurrences of long tasks can lead to sluggish page responses, animation stuttering, and other performance problems. Modern browsers can detect these tasks using the Long Tasks API:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('Long task detected:', entry);
  }
});
observer.observe({entryTypes: ['longtask']});

Code Splitting and Lazy Loading

Splitting large JavaScript bundles into smaller modules is an effective way to reduce long tasks. Use dynamic imports for on-demand loading:

// Traditional approach
import { heavyModule } from './heavyModule';

// Dynamic import approach
button.addEventListener('click', async () => {
  const { heavyFunction } = await import('./heavyModule');
  heavyFunction();
});

For React applications, use React.lazy and Suspense:

const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

Task Decomposition and Time Slicing

Break down large tasks into smaller chunks and use setTimeout or requestIdleCallback for incremental processing:

function processLargeArray(array) {
  let index = 0;
  
  function processChunk() {
    const start = performance.now();
    while (index < array.length && performance.now() - start < 10) {
      // Process individual items
      processItem(array[index++]);
    }
    
    if (index < array.length) {
      // Yield the main thread with setTimeout
      setTimeout(processChunk, 0);
    }
  }
  
  processChunk();
}

For React applications, use the Time Slicing API:

function MyComponent({ items }) {
  return (
    <ul>
      {items.map(item => (
        <React.unstable_SuspenseList revealOrder="forwards">
          <Item key={item.id} item={item} />
        </React.unstable_SuspenseList>
      ))}
    </ul>
  );
}

Utilizing Web Workers

Offload CPU-intensive tasks to Web Workers to significantly reduce main thread load:

// Main thread code
const worker = new Worker('worker.js');

worker.postMessage({ data: largeDataSet });

worker.onmessage = (event) => {
  console.log('Result:', event.data);
};

// worker.js
self.onmessage = (event) => {
  const result = heavyComputation(event.data);
  self.postMessage(result);
};

Optimizing Animation Performance

Use CSS animations and transform properties instead of JavaScript animations:

/* Before optimization */
.element {
  left: 0;
  transition: left 0.3s ease;
}

/* After optimization */
.element {
  transform: translateX(0);
  transition: transform 0.3s ease;
}

For complex animations, use requestAnimationFrame:

function animate() {
  // Animation logic
  element.style.transform = `translateX(${position}px)`;
  
  if (position < target) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

Virtual List Optimization for Long Lists

For large lists, render only items within the visible area:

function VirtualList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(
    items.length - 1,
    startIndex + Math.ceil(containerHeight / itemHeight)
  );

  return (
    <div 
      style={{ height: containerHeight, overflow: 'auto' }}
      onScroll={(e) => setScrollTop(e.currentTarget.scrollTop)}
    >
      <div style={{ height: items.length * itemHeight }}>
        {items.slice(startIndex, endIndex + 1).map((item, index) => (
          <div 
            key={item.id}
            style={{ 
              height: itemHeight,
              position: 'absolute',
              top: (startIndex + index) * itemHeight
            }}
          >
            {item.content}
          </div>
        ))}
      </div>
    </div>
  );
}

Reducing Style Calculation Complexity

Avoid wildcard selectors and deep nesting:

/* Not recommended */
div * {
  color: red;
}

/* Recommended */
.specific-class {
  color: red;
}

Use naming conventions like BEM to reduce selector complexity:

/* BEM example */
.block {}
.block__element {}
.block--modifier {}

Optimizing Event Handling

Use event delegation to reduce the number of event listeners:

// Not recommended
document.querySelectorAll('.item').forEach(item => {
  item.addEventListener('click', handleClick);
});

// Recommended
document.querySelector('.container').addEventListener('click', (e) => {
  if (e.target.matches('.item')) {
    handleClick(e);
  }
});

For frequently triggered events (e.g., scroll, resize), use debouncing or throttling:

function debounce(func, delay) {
  let timeout;
  return function() {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, arguments), delay);
  };
}

window.addEventListener('resize', debounce(handleResize, 200));

Memory Management Optimization

Clean up unused references and event listeners promptly:

class Component {
  constructor() {
    this.data = fetchData();
    window.addEventListener('resize', this.handleResize);
  }
  
  // Must implement cleanup logic
  cleanup() {
    this.data = null;
    window.removeEventListener('resize', this.handleResize);
  }
}

Avoid common memory leak patterns:

// References in closures
function createHeavyObject() {
  const largeObject = createLargeObject();
  
  return function() {
    // Accidentally retains reference to largeObject
    console.log('Operation');
  };
}

// Better approach
function createLightweightClosure() {
  const neededData = extractNeededData(createLargeObject());
  
  return function() {
    console.log(neededData);
  };
}

Server-Side Rendering Optimization

For complex initial renders, consider server-side rendering:

// Express server example
app.get('/', (req, res) => {
  const appContent = ReactDOMServer.renderToString(<App />);
  
  res.send(`
    <!DOCTYPE html>
    <html>
      <head>
        <title>SSR Example</title>
      </head>
      <body>
        <div id="root">${appContent}</div>
        <script src="/client.bundle.js"></script>
      </body>
    </html>
  `);
});

Preloading Critical Resources

Use rel="preload" to load critical resources early:

<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="main.js" as="script">

For font files:

<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

Avoiding Forced Synchronous Layouts

Batch style property reads to avoid layout thrashing:

// Not recommended - causes forced synchronous layout
function resizeAllItems() {
  const items = document.querySelectorAll('.item');
  for (let i = 0; i < items.length; i++) {
    items[i].style.height = items[i].offsetWidth + 'px';
  }
}

// Recommended - read first, then write
function resizeAllItemsOptimized() {
  const items = document.querySelectorAll('.item');
  const widths = [];
  
  // Batch reads
  for (let i = 0; i < items.length; i++) {
    widths[i] = items[i].offsetWidth;
  }
  
  // Batch writes
  for (let i = 0; i < items.length; i++) {
    items[i].style.height = widths[i] + 'px';
  }
}

Using Efficient DOM Manipulation Methods

Minimize DOM operations:

// Not recommended
for (let i = 0; i < 100; i++) {
  document.body.appendChild(document.createElement('div'));
}

// Recommended - use DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  fragment.appendChild(document.createElement('div'));
}
document.body.appendChild(fragment);

Optimizing Third-Party Script Loading

Load non-critical third-party scripts asynchronously:

<script src="analytics.js" async defer></script>

Or use Intersection Observer for lazy loading:

const observer = new IntersectionObserver((entries) => {
  if (entries[0].isIntersecting) {
    const script = document.createElement('script');
    script.src = 'analytics.js';
    document.body.appendChild(script);
    observer.disconnect();
  }
});

observer.observe(document.querySelector('.footer'));

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.