Phase 6 · Advanced & ProfessionalModule 38~48 min read

Performance, Memory & Workers

Measure and optimize runtime, rendering, loading, and memory while moving heavy work off the main thread.

What you'll learn

Performance work begins with user-centered measurement. Improve algorithms and payloads first, remove main-thread contention, and use workers only when profiling shows CPU-bound work.

By the end of this lesson, you'll be able to:

  • Profile CPU, memory, loading, and rendering
  • Find common JavaScript memory leaks
  • Move suitable work to workers

Core mental model

Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.

ConceptWhat it meansDecision rule
BudgetNumeric limit tied to user experienceSet before optimization and enforce in CI
Heap retentionReachable references keep memory aliveInspect retaining paths, not just allocation counts
WorkerSeparate thread communicating by messagesUse for substantial CPU work with transferable data

Professional workflow

Build the behavior in small, observable steps. Each step should leave something you can inspect or test.

  1. Describe the performance budget boundary: inputs, outputs, state, timing, and expected failures.
  2. Implement the smallest correct path with names that expose intent.
  3. Add edge cases and failure handling before introducing abstractions.
  4. Verify behavior with realistic data and one deliberately adversarial example.
  5. Refactor only after the observable behavior is protected.

Make behavior observable

Before optimizing or abstracting, make inputs, outputs, state changes, timing, and failure paths visible. JavaScript becomes much easier to reason about when hidden work is exposed.

Guided code lab

Measure one operation

Performance marks produce a named duration that can be compared after changes.

measure.js
performance.mark("report-start");
const report = buildReport(records);
performance.mark("report-end");
performance.measure("build-report", "report-start", "report-end");
console.log(performance.getEntriesByName("build-report")[0].duration);

Move CPU work to a worker

The main thread sends serializable data and receives a result without blocking interaction.

report-worker.js
// main.js
const worker = new Worker(new URL("./worker.js", import.meta.url), { type: "module" });
worker.postMessage(records);
worker.addEventListener("message", event => renderReport(event.data));

// worker.js
self.addEventListener("message", event => {
  self.postMessage(buildReport(event.data));
});

Production practice

Contract

Write down the performance budget quality target, ownership boundary, evidence, and acceptable tradeoffs.

Verification

Use repeatable automated checks plus a realistic end-to-end scenario; include failure and recovery behavior.

Operations

Track a small set of user-centered signals and revisit the decision when evidence changes.

Common failure mode

Micro-optimizing syntax before measuring can increase complexity while leaving network, algorithmic, or rendering bottlenecks untouched.

Independent workshop

Profile and optimize a large progress dashboard.

Your finished workshop must include:

  • A before/after performance trace
  • One verified leak fix
  • A worker or documented reason not to use one

Definition of done

Demonstrate the happy path and at least two edge cases, keep responsibilities separated, and add a short note explaining one design choice.

Recap & quick check

Key takeaways

  • Measure user-centered outcomes
  • Algorithm choice dominates syntax tweaks
  • Reachability explains leaks
  • Workers trade messaging cost for parallel CPU work

Quick check

1. What should happen before optimization?

2. What keeps an unused object alive?

3. What is worker communication based on?

Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.