Zones for JavaScript

0.16.1 · maintenance · verified Sun Apr 19

Zone.js implements the 'Zone' concept for JavaScript, inspired by Dart, providing an execution context that persists across synchronous and asynchronous tasks. It acts like thread-local storage for JavaScript VMs, allowing developers to track context (like request IDs or user data) across complex asynchronous operations. Zone.js achieves this by monkey-patching most standard web APIs (e.g., DOM events, `XMLHttpRequest`, `setTimeout`, `Promise`) and Node.js APIs (`EventEmitter`, `fs`). The current stable version is `0.16.1`, although the project's recent releases align with Angular's major versions (e.g., `v21.2.9`). While historically a core part of Angular's change detection, Angular is transitioning to a zoneless model, using Signals for reactivity. Consequently, Zone.js is no longer accepting new features or low-priority bug fixes, with critical fixes limited to Angular's direct use cases, and its use outside Angular is strongly discouraged.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to initialize Zone.js and use `Zone.current.fork()` to create custom execution contexts that persist properties across synchronous and patched asynchronous tasks like `setTimeout` and `Promise` callbacks.

import 'zone.js'; // Ensure zone.js is loaded to patch global APIs

// The global Zone object is now available
console.log(`Initial Root Zone Name: ${Zone.current.name}`);

// Create a new zone with custom properties
const requestScopedZone = Zone.current.fork({
  name: 'requestZone',
  properties: {
    requestId: 'req-' + Math.random().toString(36).substring(2, 9)
  }
});

console.log(`
Starting a new request simulation...`);

// Run a synchronous task within the new zone
requestScopedZone.run(() => {
  const currentRequestId = Zone.current.get('requestId');
  console.log(`  Inside requestZone (sync): Request ID = ${currentRequestId}`);

  // Schedule an asynchronous task. Zone.js patches setTimeout to preserve context.
  setTimeout(() => {
    // This callback will automatically run within 'requestScopedZone'
    console.log(`  Inside requestZone (async setTimeout): Request ID = ${Zone.current.get('requestId')}`);
    if (Zone.current.get('requestId') === currentRequestId) {
      console.log('  Async context correctly preserved!');
    }
  }, 50);

  // Another async operation (e.g., a Promise)
  Promise.resolve().then(() => {
    console.log(`  Inside requestZone (async Promise): Request ID = ${Zone.current.get('requestId')}`);
  });
});

console.log(`
Back in Root Zone: Current Zone Name = ${Zone.current.name}`);

// Demonstrating another independent zone
const userContextZone = Zone.current.fork({
  name: 'userContextZone',
  properties: {
    userId: 'user-' + Math.floor(Math.random() * 1000)
  }
});

userContextZone.run(() => {
  console.log(`
  Inside userContextZone: User ID = ${Zone.current.get('userId')}`);
  setTimeout(() => {
    console.log(`  Inside userContextZone (async): User ID = ${Zone.current.get('userId')}`);
  }, 20);
});

view raw JSON →