How Chromium Terminates Idle Extension Service Workers

This article provides an overview of how the Chromium web browser manages the lifecycle of Manifest V3 extension service workers, focusing specifically on how it terminates workers when asynchronous events remain idle. To optimize memory and CPU usage, Chromium enforces strict timeout mechanisms—primarily a 30-second inactivity timer and a 5-minute single-request limit—that forcefully shut down service workers when asynchronous tasks stall or fail to resolve.

The Core Timeout Mechanisms

Chromium uses two main timers to govern extension service worker lifetimes and handle idle states:

  • The 30-Second Inactivity Timer: After an event is dispatched to the service worker, Chromium starts a 30-second countdown. If no new events occur and no recognized asynchronous operations are pending, the browser considers the worker idle and terminates it.
  • The 5-Minute Single-Request Timer: When an event triggers an asynchronous operation, Chromium pauses the 30-second termination timer to allow the task to finish. However, if that single asynchronous event remains pending (idle) for more than 5 minutes without resolving, Chromium assumes the task is stuck and forcefully terminates the service worker.

How Different Asynchronous Events Are Handled

Not all asynchronous operations are treated equally by Chromium's lifecycle manager. How the browser handles an idle asynchronous event depends on how that event was initiated.

1. Extension API Calls

since Chromium 110, calling most asynchronous chrome.* APIs resets the 30-second inactivity timer. While the API call is actively waiting for a response (remaining idle in the background), the service worker is kept alive. If the API call hangs and fails to resolve within the 5-minute maximum limit, the worker is terminated.

2. Standard Web APIs and Unattached Promises

Standard JavaScript asynchronous operations—such as setTimeout, setInterval, or a basic Promise—do not automatically keep a service worker alive. If a service worker initiates a setTimeout set for 45 seconds and does nothing else, Chromium sees the worker as idle. The 30-second inactivity timer will expire, and the worker will be terminated before the timeout completes.

To keep the worker alive during a standard Promise, the Promise must be passed to the event.waitUntil() method inside a standard Service Worker event (like install or activate), which subjects it to the 5-minute limit instead of the 30-second limit.

3. Fetch Requests

When a fetch() request is initiated, Chromium keeps the service worker alive while waiting for the network response. If the server hangs and the connection remains idle without closing, the 5-minute single-request timer will eventually trigger and kill the worker.

4. Long-Lived Connections (Ports and WebSockets)

Asynchronous communication channels like chrome.runtime.connect (messaging ports) and WebSocket connections have specific idle rules:

  • Messaging Ports: Opening a port keeps the service worker alive, provided messages are actively exchanged. In newer versions of Chromium, merely holding a port open without traffic will allow the 30-second idle timer to terminate the worker unless the port is connected to a native messaging host.
  • WebSockets: Active WebSocket connections reset the 30-second timer whenever a message is sent or received. If the WebSocket remains completely idle for 30 seconds, the service worker is terminated, which automatically closes the socket.

Consequences of Termination

When Chromium terminates a service worker due to an idle asynchronous event, the shutdown is immediate:

  • Aborted Operations: Any unresolved Promises, pending fetch requests, or active timers are instantly aborted without throwing catchable errors.
  • Memory Wipe: All global variables and in-memory states are destroyed.
  • Closed Connections: Any open messaging ports or network sockets are severed.

To prevent data loss from sudden termination, extension developers must avoid relying on global variables during asynchronous workflows and instead persist state immediately using chrome.storage or IndexedDB.