Docs For AI
Performance

Code Optimization

Algorithm efficiency, memory management, and runtime performance optimization

Code Optimization

Writing efficient code improves both performance and maintainability. This covers algorithm optimization, memory management, and JavaScript-specific techniques.

Algorithm Efficiency

Time Complexity Awareness

// O(n²) - Avoid for large datasets
function findDuplicatesSlow(arr: number[]): number[] {
  const duplicates: number[] = [];
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j] && !duplicates.includes(arr[i])) {
        duplicates.push(arr[i]);
      }
    }
  }
  return duplicates;
}

// O(n) - Use Set for O(1) lookups
function findDuplicatesFast(arr: number[]): number[] {
  const seen = new Set<number>();
  const duplicates = new Set<number>();

  for (const num of arr) {
    if (seen.has(num)) {
      duplicates.add(num);
    }
    seen.add(num);
  }

  return [...duplicates];
}

Efficient Data Structures

// Use Map for frequent lookups
const userMap = new Map<string, User>();
userMap.set(user.id, user);
const user = userMap.get(id); // O(1)

// Use Set for membership checks
const activeIds = new Set<string>(activeUsers.map(u => u.id));
const isActive = activeIds.has(userId); // O(1)

// Use proper indexing for searches
const usersByEmail = new Map(users.map(u => [u.email, u]));
const user = usersByEmail.get(email);

Memoization

// Simple memoization
function memoize<T extends (...args: any[]) => any>(fn: T): T {
  const cache = new Map<string, ReturnType<T>>();

  return ((...args: Parameters<T>) => {
    const key = JSON.stringify(args);

    if (cache.has(key)) {
      return cache.get(key)!;
    }

    const result = fn(...args);
    cache.set(key, result);
    return result;
  }) as T;
}

// With LRU cache for bounded memory
class LRUCache<K, V> {
  private cache = new Map<K, V>();

  constructor(private maxSize: number) {}

  get(key: K): V | undefined {
    if (!this.cache.has(key)) return undefined;

    // Move to end (most recently used)
    const value = this.cache.get(key)!;
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
  }

  set(key: K, value: V): void {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.maxSize) {
      // Remove oldest (first) entry
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }
}

Memory Management

Avoiding Memory Leaks

// Bad - event listener leak
class Component {
  constructor() {
    window.addEventListener('resize', this.handleResize);
  }

  handleResize = () => {
    // ...
  };

  // Missing cleanup!
}

// Good - proper cleanup
class Component {
  private abortController = new AbortController();

  constructor() {
    window.addEventListener('resize', this.handleResize, {
      signal: this.abortController.signal,
    });
  }

  handleResize = () => {
    // ...
  };

  destroy() {
    this.abortController.abort();
  }
}

// React hook pattern
function useWindowResize(callback: () => void) {
  useEffect(() => {
    const handler = () => callback();
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, [callback]);
}

Weak References

// Use WeakMap for object-keyed caches
// Allows garbage collection when key is no longer referenced
const cache = new WeakMap<object, ComputedData>();

function getComputedData(obj: object): ComputedData {
  if (!cache.has(obj)) {
    cache.set(obj, expensiveComputation(obj));
  }
  return cache.get(obj)!;
}

// Use WeakSet for membership tracking
const processedItems = new WeakSet<Item>();

function processOnce(item: Item) {
  if (processedItems.has(item)) return;
  processedItems.add(item);
  // Process item...
}

Object Pooling

// Reuse objects instead of creating new ones
class ObjectPool<T> {
  private pool: T[] = [];

  constructor(
    private factory: () => T,
    private reset: (obj: T) => void,
    private initialSize = 10
  ) {
    for (let i = 0; i < initialSize; i++) {
      this.pool.push(factory());
    }
  }

  acquire(): T {
    return this.pool.pop() ?? this.factory();
  }

  release(obj: T): void {
    this.reset(obj);
    this.pool.push(obj);
  }
}

// Usage for frequent allocations
const vectorPool = new ObjectPool(
  () => ({ x: 0, y: 0 }),
  (v) => { v.x = 0; v.y = 0; }
);

function calculateVectors(points: Point[]) {
  const vectors = points.map(() => vectorPool.acquire());
  // Use vectors...
  vectors.forEach(v => vectorPool.release(v));
}

String Operations

// Bad - string concatenation in loop
function buildStringSlow(items: string[]): string {
  let result = '';
  for (const item of items) {
    result += item + ', ';  // Creates new string each iteration
  }
  return result;
}

// Good - use array join
function buildStringFast(items: string[]): string {
  return items.join(', ');
}

// For complex string building
function buildHTML(items: Item[]): string {
  const parts: string[] = [];
  parts.push('<ul>');
  for (const item of items) {
    parts.push(`<li>${item.name}</li>`);
  }
  parts.push('</ul>');
  return parts.join('');
}

Array Operations

// Prefer for...of for iteration (when you don't need index)
for (const item of items) {
  process(item);
}

// Use forEach for side effects
items.forEach(item => console.log(item));

// Chain methods efficiently
const result = items
  .filter(item => item.active)
  .map(item => item.value);

// For very large arrays, consider single pass
const result: number[] = [];
for (const item of items) {
  if (item.active) {
    result.push(item.value);
  }
}

// Avoid creating intermediate arrays when possible
// Bad
const sum = items.map(i => i.value).reduce((a, b) => a + b, 0);

// Good
const sum = items.reduce((acc, item) => acc + item.value, 0);

Async Optimization

Parallel Execution

// Sequential - slow
async function fetchAllSequential(ids: string[]) {
  const results = [];
  for (const id of ids) {
    results.push(await fetchItem(id));
  }
  return results;
}

// Parallel - fast
async function fetchAllParallel(ids: string[]) {
  return Promise.all(ids.map(id => fetchItem(id)));
}

// Parallel with concurrency limit
async function fetchWithLimit(ids: string[], limit = 5) {
  const results: Item[] = [];
  const executing: Promise<void>[] = [];

  for (const id of ids) {
    const promise = fetchItem(id).then(item => {
      results.push(item);
    });

    executing.push(promise);

    if (executing.length >= limit) {
      await Promise.race(executing);
      executing.splice(executing.findIndex(p => p === promise), 1);
    }
  }

  await Promise.all(executing);
  return results;
}

AbortController for Cancellation

async function fetchWithTimeout(url: string, timeout = 5000) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeout);

  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } finally {
    clearTimeout(timeoutId);
  }
}

// Cancel previous request on new one
let currentController: AbortController | null = null;

async function search(query: string) {
  currentController?.abort();
  currentController = new AbortController();

  try {
    const response = await fetch(`/api/search?q=${query}`, {
      signal: currentController.signal,
    });
    return await response.json();
  } catch (error) {
    if (error.name === 'AbortError') {
      return null; // Cancelled, not an error
    }
    throw error;
  }
}

Web Workers

Offload heavy computation to background threads.

// worker.ts
self.onmessage = (event: MessageEvent<WorkerInput>) => {
  const { data, type } = event.data;

  switch (type) {
    case 'PROCESS_DATA':
      const result = heavyComputation(data);
      self.postMessage({ type: 'RESULT', result });
      break;
  }
};

function heavyComputation(data: number[]): number {
  // CPU-intensive work
  return data.reduce((sum, n) => sum + Math.sqrt(n), 0);
}

// main.ts
const worker = new Worker(new URL('./worker.ts', import.meta.url));

function processInBackground(data: number[]): Promise<number> {
  return new Promise((resolve) => {
    worker.onmessage = (event) => {
      if (event.data.type === 'RESULT') {
        resolve(event.data.result);
      }
    };
    worker.postMessage({ type: 'PROCESS_DATA', data });
  });
}

Best Practices

Code Optimization Guidelines

  1. Profile before optimizing - measure, don't guess
  2. Choose appropriate data structures (Map, Set)
  3. Avoid premature optimization
  4. Clean up event listeners and subscriptions
  5. Use WeakMap/WeakSet for object caches
  6. Prefer array methods but understand their cost
  7. Run heavy computation in Web Workers
  8. Implement request cancellation with AbortController

On this page