Usar Service Workers
====================

Este artículo brinda información sobre cómo comenzar con el *service worker*, incluida la arquitectura básica, el registro de un *service worker*, el proceso de instalación y activación de un nuevo *service worker*, la actualización de tu *service worker*, el control de caché y las respuestas personalizadas, todo en el contexto de una aplicación simple, con funcionalidad fuera de línea.

La premisa del service worker
-----------------------------

Un problema primordial del que los usuarios de la web han adolecido durante años es la pérdida de conectividad. La mejor aplicación web del mundo proporcionará una experiencia de usuario terrible si no la puedes descargar. Ha habido varios intentos de crear tecnologías para resolver este problema, y algunos de los problemas se han resuelto. Pero el problema primordial es que todavía no existe un buen mecanismo de control general para el almacenamiento en caché de activos y las solicitudes de red personalizadas.

El intento anterior, *AppCache*, parecía ser una buena idea porque te permitía especificar activos para almacenar en caché con mucha facilidad. Sin embargo, hizo muchas suposiciones sobre lo que estabas tratando de hacer y luego se rompió horriblemente cuando tu aplicación no siguió exactamente esas suposiciones. Lee el documento de Jake Archibald (desafortunadamente mal titulado pero bien escrito) Application Cache is a Douchebag para obtener más detalles.

**Nota:** A partir de Firefox 84, se eliminó *AppCache* (Error 1619673 en Firefox). También se ha eliminado de Chromium 95 y está obsoleto en Safari.

El *service worker* finalmente debería solucionar estos problemas. La sintaxis del *service worker* es más compleja que la de *AppCache*, pero la compensación es que puedes usar JavaScript para controlar su comportamiento implícito en *AppCache* con un buen grado de fina granularidad, lo que te permite manejar este problema y muchos más. Al usar un *service worker*, puedes configurar fácilmente una aplicación para usar activos almacenados en caché primero, proporcionando así una experiencia predeterminada incluso cuando estás desconectado, antes de obtener más datos de la red (comúnmente conocido como Primero sin conexión). Esto ya está disponible con las aplicaciones nativas, que es una de las principales razones por las que las aplicaciones nativas a menudo se eligen en lugar de las aplicaciones web.

Configuración para jugar con el service worker
----------------------------------------------

En estos días, el *service worker* está habilitado de forma predeterminada en todos los navegadores modernos. Para ejecutar código con el *service worker*, deberás entregar tu código a través de HTTPS: el *service worker*, por razones de seguridad, está restringido a ejecutarse a través de HTTPS. Por lo tanto, GitHub es un buen lugar para alojar experimentos, ya que admite HTTPS. Para facilitar el desarrollo local, los navegadores también consideran `localhost` como un origen seguro.

Arquitectura básica
-------------------

Con el *service worker*, generalmente se observan los siguientes pasos para la configuración básica:

1. La URL del *service worker* se obtiene y registra a través de `serviceWorkerContainer.register()`.
2. Si tiene éxito, el *service worker* se ejecuta en `ServiceWorkerGlobalScope` (en-US); esto es básicamente un tipo especial de contexto de trabajo, que se ejecuta fuera del hilo principal de ejecución del script, sin acceso al DOM.
3. El *service worker* ahora está listo para procesar eventos.
4. Se intenta la instalación del *worker* cuando se accede posteriormente a las páginas controladas por el *service worker*. Un evento de instalación siempre es el primero que se envía a un *service worker* (esto se puede usar para iniciar el proceso de completar una IndexedDB «base de datos indexada» y almacenar en caché los activos del sitio). Este es realmente el mismo tipo de procedimiento que instalar una aplicación nativa o Firefox OS: hace que todo esté disponible para usar sin conexión.
5. Cuando se completa el controlador `oninstall`, se considera que el *service worker* está instalado.
6. Lo siguiente es la activación. Cuando se instala el *service worker*, recibe un evento de activación. El uso principal de `onactivate` es para la limpieza de los recursos utilizados en versiones anteriores de un script del *service worker*.
7. El *service worker* ahora controlará las páginas, pero solo aquellas que se abran después de que `register()` tenga éxito. En otras palabras, los documentos se deberán volver a cargar para controlarlos realmente, porque un documento comienza con o sin un *service worker* y lo mantiene durante toda su vida.

![](/es/docs/Web/API/Service_Worker_API/Using_Service_Workers/sw-lifecycle.png)

El siguiente gráfico muestra un resumen de los eventos de *service worker* disponibles:

![install, activate, message, fetch, sync, push](/es/docs/Web/API/Service_Worker_API/Using_Service_Workers/sw-events.png)

Demostración del service worker
-------------------------------

Para demostrar los conceptos básicos de registro e instalación de un *service worker*, hemos creado una demostración simple llamada *service worker* simple, que es una simple galería de imágenes de Star Wars Lego. Utiliza una función impulsada por promesas para leer datos de imagen de un objeto JSON y cargar las imágenes usando Ajax, antes de mostrar las imágenes en una línea hacia abajo en la página. Hemos mantenido las cosas estáticas y simples por ahora. También registra, instala y activa un *service worker*, y cuando los navegadores admiten más especificaciones, almacenará en caché todos los archivos necesarios para que funcione sin conexión.

![Las palabras Star Wars seguidas de una imagen de una versión Lego del personaje Darth Vader](/es/docs/Web/API/Service_Worker_API/Using_Service_Workers/demo-screenshot.png)

Puedes ver el código fuente en GitHub y el Sencillo *service worker* ejecutándose en vivo.

### Registra a tu worker

El primer bloque de código en el archivo JavaScript de nuestra aplicación, `app.js`, es el siguiente. Este es nuestro punto de entrada en el uso del *service worker*.


```js
const registerServiceWorker = async () => {
  if ("serviceWorker" in navigator) {
    try {
      const registration = await navigator.serviceWorker.register("/sw.js", {
        scope: "/",
      });
      if (registration.installing) {
        console.log("Instalando el Service worker");
      } else if (registration.waiting) {
        console.log("Service worker instalado");
      } else if (registration.active) {
        console.log("Service worker activo");
      }
    } catch (error) {
      console.error(`Falló el registro con el ${error}`);
    }
  }
};

// …

registerServiceWorker();
```


1. El bloque if realiza una prueba de detección de características para asegurarse de que el *service worker* sea compatible antes de intentar registrar uno.
2. A continuación, usamos la función `ServiceWorkerContainer.register()` para registrar el *service worker* para este sitio, que solo es un archivo JavaScript que reside dentro de nuestra aplicación (ten en cuenta que esta es la URL del archivo relativa al origen , no el archivo JS que hace referencia a él).
3. El parámetro `scope` es opcional y se puede usar para especificar el subconjunto de tu contenido que deseas controle el *service worker*. En este caso, hemos especificado `'/'`, lo cual significa todo el contenido bajo el origen de la aplicación. Si lo omites, tendrá este valor predeterminado de todos modos, pero lo especificamos aquí con fines ilustrativos.

Esto registra un *service worker*, que se ejecuta en un contexto de trabajador y, por lo tanto, no tiene acceso al DOM. Luego ejecuta el código en el *service worker* fuera de tus páginas normales para controlar su carga.

Un solo *service worker* puede controlar muchas páginas. Cada vez que se carga una página dentro de su alcance, el *service worker* se instala en esa página y opera en ella. Por lo tanto, ten en cuenta que debes tener cuidado con las variables globales en el script del *service worker*: cada página no tiene su propio trabajador único.

**Nota:** Tu *service worker* funciona como un servidor proxy, lo que te permite modificar solicitudes y respuestas, reemplazarlas con elementos de su propio caché y más.

**Nota:** Una gran cosa acerca del *service worker* es que si usas la detección de funciones como se muestra arriba, los navegadores que no son compatibles con los *service workers* pueden usar tu aplicación en línea de la manera normal esperada. Además, si usas *AppCache* y SW en una página, los navegadores que no admiten SW pero sí *AppCache* lo usarán, y los navegadores que admiten ambos ignorarán *AppCache* y dejarán que SW tome el control.

#### ¿Por qué mi service worker no se registra?

Esto se podría deber a las siguientes razones:

1. No estás ejecutando tu aplicación a través de HTTPS.
2. La ruta a tu archivo del *service worker* no está escrita correctamente — se debe escribir en relación con el origen, no con el directorio raíz de tu aplicación. En nuestro ejemplo, el trabajador está en `https://bncb2v.csb.app/sw.js` y la raíz de la aplicación es `https://bncb2v.csb.app/`. Pero la ruta se debe escribir como `/sw.js`.
3. Tampoco está permitido apuntar a un *service worker* de un origen diferente al de tu aplicación.

![](/es/docs/Web/API/Service_Worker_API/Using_Service_Workers/important-notes.png)

También ten en cuenta:

* El *service worker* solo capturará las solicitudes de los clientes bajo el alcance del *service worker*.
* El alcance máximo para un *service worker* es la ubicación del trabajador.
* Si tu *service worker* está activo en un cliente al que se atiende con el encabezado `Service-Worker-Allowed`, puedes especificar una lista de alcances máximos para ese trabajador.
* En Firefox, las APIs de *Service Worker* están ocultas y no se pueden usar cuando el usuario está en modo de navegación privada.
### Instalar y activar: llena tu caché

Después de que tu *service worker* esté registrado, el navegador intentará instalar y luego activar el *service worker* para tu página/sitio.

El evento `install` se activa cuando una instalación se completa con éxito. El evento `install` generalmente se usa para llenar las capacidades de almacenamiento en caché sin conexión de tu navegador con los activos que necesita para ejecutar tu aplicación sin conexión. Para hacer esto, usamos la API de almacenamiento de *Service Worker*: `cache` (en-US) — un objeto global en *Service Worker* que nos permite almacenar los activos entregados por las respuestas y con clave de sus solicitudes. Esta API funciona de manera similar a la memoria caché estándar del navegador, pero es específica para tu dominio. Persiste hasta que le dices que no lo haga — nuevamente, tienes el control total.

Así es como nuestro *service worker* maneja el evento `install`:


```js
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v1");
  await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",
      "/star-wars-logo.jpg",
      "/gallery/bountyHunters.jpg",
      "/gallery/myLittleVader.jpg",
      "/gallery/snowTroopers.jpg",
    ]),
  );
});
```


1. Aquí agregamos un detector de eventos `install` al *service worker* (por lo tanto, `self`), y luego encadenamos un método `ExtendableEvent.waitUntil()` (en-US) al evento; esto garantiza que el *service worker* no se instale hasta que el código dentro de `waitUntil()` haya ocurrido con éxito.
2. Dentro de `addResourcesToCache` usamos el método `caches.open()` para crear un nuevo caché llamado `v1`, que será la versión 1 de nuestro caché de recursos del sitio. Luego llamamos a una función que llama a `addAll()` en el caché creado, que para su parámetro toma un arreglo de URLs relativas al origen de todos los recursos que deseas almacenar en caché.
3. Si se rechaza la promesa, la instalación falla y el trabajador no hará nada. Esto está bien, ya que puedes corregir tu código y luego intentarlo de nuevo la próxima vez que se registre.
4. Después de una instalación exitosa, el *service worker* se activa. Esto no tiene mucho de un uso distinto la primera vez que se instala/activa tu *service worker*, pero significa más cuando se actualiza el *service worker* (consulta la sección Actualizar tu *service worker* más adelante).

**Nota:** localStorage funciona de manera similar a la memoria caché del *service worker*, pero es síncrono, por lo que no está permitido en el *service worker*.

**Nota:** IndexedDB se puede usar dentro de un *service worker* para el almacenamiento de datos si lo requieres.

### Respuestas personalizadas a solicitudes

Ahora que tienes los activos de tu sitio almacenados en caché, debes decir al *service worker* que haga algo con el contenido almacenado en caché. Esto se hace fácilmente con el evento `fetch`.

![](/es/docs/Web/API/Service_Worker_API/Using_Service_Workers/sw-fetch.png)

Un evento `fetch` se activa cada vez que se recupera cualquier recurso controlado por un *service worker*, lo que incluye los documentos dentro del alcance especificado y cualquier recurso al que se haga referencia en esos documentos (por ejemplo, si `index.html` hace una solicitud de origen cruzado para incrustar una imagen, que todavía pasa por su *service worker*).

Puedes adjuntar un detector de eventos `fetch` al *service worker*, luego llamar al método `respondWith()` en el evento para capturar nuestras respuestas HTTP y actualizarlas con tu propia magia.


```js
self.addEventListener("fetch", (event) => {
  event
    .respondWith
    // la magia va aquí
    ();
});
```


Podríamos empezar respondiendo con el recurso cuya URL coincida con la de la solicitud de red, en cada caso:


```js
self.addEventListener("fetch", (event) => {
  event.respondWith(caches.match(event.request));
});
```


`caches.match(event.request)` nos permite hacer coincidir cada recurso solicitado de la red con el recurso equivalente disponible en caché, si hay uno coincidente disponible. La coincidencia se realiza a través de URL y varios encabezados, al igual que con las solicitudes HTTP normales.

Veamos algunas otras opciones que tenemos al definir nuestra magia (consulta nuestra documentación de la API Fetch para obtener más información sobre los objetos `Request` y `Response`.)

1. El constructor `Response()` te permite crear una respuesta personalizada. En este caso, solo estamos devolviendo una cadena de texto simple:

```js
new Response("¡Hola desde tu amigable vecindario del service worker!");
```

2. Esta `Response` más compleja a continuación muestra que, opcionalmente, puedes pasar un conjunto de encabezados con tu respuesta, emulando los encabezados de respuesta HTTP estándar. Aquí solo le estamos diciendo al navegador cuál es el tipo de contenido de nuestra respuesta sintética:

```js
new Response(
  "<p>¡Hola desde tu amigable vecindario del service worker!</p>",
  {
    headers: { "Content-Type": "text/html" },
  },
);
```

3. Si no se encontró una coincidencia en caché, le puedes decir al navegador que `fetch()` la solicitud de red predeterminada para ese recurso, para obtener el nuevo recurso de la red si está disponible:

```js
fetch(event.request);
```

4. Si no se encontró una coincidencia en caché y la red no está disponible, puedes hacer coincidir la solicitud con algún tipo de página de respaldo predeterminada como respuesta usando `match()`  (en-US), como esta:

```js
caches.match("./fallback.html");
```

5. Puedes recuperar mucha información sobre cada solicitud llamando a los parámetros del objeto `Request` devuelto por `FetchEvent`:

```js
event.request.url;
event.request.method;
event.request.headers;
event.request.body;
```

Recuperar solicitudes fallidas
------------------------------

Entonces `caches.match(event.request)` es excelente cuando hay una coincidencia en caché del *service worker*, pero ¿qué pasa con los casos en los que no hay una coincidencia? Si no proporcionamos ningún tipo de manejo de fallas, nuestra promesa se resolvería con `undefined` y no tendríamos nada devuelto.

Afortunadamente, la estructura basada en promesas del *service worker* hace que sea trivial brindar más opciones hacia el éxito. Podríamos hacer esto:


```js
const cacheFirst = async (request) => {
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }
  return fetch(request);
};

self.addEventListener("fetch", (event) => {
  event.respondWith(cacheFirst(event.request));
});
```


Si los recursos no están en la memoria caché, se solicitan desde la red.

Si fuéramos realmente inteligentes, no solo solicitaríamos el recurso de la red; ¡también lo guardaríamos en caché para que las solicitudes posteriores de ese recurso también se puedan recuperar sin conexión! Esto significaría que si se agregaran imágenes adicionales a la galería de Star Wars, nuestra aplicación podría capturarlas automáticamente y almacenarlas en caché. Lo siguiente haría el truco:


```js
const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async (request) => {
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }
  const responseFromNetwork = await fetch(request);
  putInCache(request, responseFromNetwork.clone());
  return responseFromNetwork;
};

self.addEventListener("fetch", (event) => {
  event.respondWith(cacheFirst(event.request));
});
```


Si la URL de la solicitud no está disponible en la memoria caché, solicitamos el recurso de la solicitud de red con `await fetch(request)`. Después de eso, colocamos en caché un clon de la respuesta. La función `putInCache` usa `caches.open('v1')` y `cache.put()` para agregar el recurso a la caché. La respuesta original se devuelve al navegador para que se proporcione a la página que la llamó.

La clonación de la respuesta es necesaria porque los flujos de solicitud y respuesta solo se pueden leer una vez. Para devolver la respuesta al navegador y ponerla en caché la tenemos que clonar. Entonces, el original se devuelve al navegador y el clon se envía a caché. Cada uno se lee una vez.

Lo que puede parecer un poco extraño es que no se espera la promesa devuelta por `putInCache`. Pero la razón es que no queremos esperar hasta que el clon de respuesta se haya agregado a la caché antes de devolver una respuesta.

El único problema que tenemos ahora es que si la solicitud no coincide con nada en caché y la red no está disponible, nuestra solicitud seguirá fallando. Proporcionemos un respaldo predeterminado para que, pase lo que pase, el usuario al menos obtenga algo:


```js
const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
  // Primero intenta obtener el recurso desde caché
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }

  // A continuación, intenta obtener el recurso desde la red
  try {
    const responseFromNetwork = await fetch(request);
    // la respuesta solo se puede usar una vez
    // necesitamos guardar el clon para poner una copia en caché
    // y servir el segundo
    putInCache(request, responseFromNetwork.clone());
    return responseFromNetwork;
  } catch (error) {
    const fallbackResponse = await caches.match(fallbackUrl);
    if (fallbackResponse) {
      return fallbackResponse;
    }
    // cuando incluso la respuesta alternativa no está disponible,
    // no hay nada que podamos hacer, pero siempre debemos
    // devolver un objeto Response
    return new Response("Ocurrió un error de red", {
      status: 408,
      headers: { "Content-Type": "text/plain" },
    });
  }
};

self.addEventListener("fetch", (event) => {
  event.respondWith(
    cacheFirst({
      request: event.request,
      fallbackUrl: "/gallery/myLittleVader.jpg",
    }),
  );
});
```


Hemos optado por esta imagen alternativa porque las únicas actualizaciones que probablemente fallarán son las imágenes nuevas, ya que todo lo demás depende de la instalación en el detector de eventos `install` que vimos anteriormente.

Precarga de navegación del service worker
-----------------------------------------

 Si está habilitada, la función de precarga de navegación comienza a descargar recursos tan pronto como se realiza la solicitud de recuperación y en paralelo con el inicio del *service worker*.
 Esto garantiza que la descarga comience de inmediato al navegar a una página, en lugar de tener que esperar hasta que se inicie el *service worker*.
 Ese retraso ocurre en muy raras ocasiones, pero es inevitable cuando ocurre y puede ser significativo.

Primero, la función debe estar habilitada durante la activación del *service worker*, usando `registration.navigationPreload.enable()` (en-US):


```js
const enableNavigationPreload = async () => {
  if (self.registration.navigationPreload) {
    // ¡Habilitar precargas de navegación!
    await self.registration.navigationPreload.enable();
  }
};

self.addEventListener("activate", (event) => {
  event.waitUntil(enableNavigationPreload());
});
```


Luego usa `event.preloadResponse` (en-US) para esperar a que el recurso precargado se termine de descargar en el controlador de eventos `fetch`.

Continuando con el ejemplo de las secciones anteriores, insertamos el código para esperar el recurso precargado después de la verificación de la caché y antes de recuperarlo de la red si eso no tiene éxito.

El nuevo proceso es:

1. Comprobar la caché
2. Esperar en `event.preloadResponse`, que se pasa como `preloadResponsePromise` a la función `cacheFirst`.
 Guardar en caché el resultado si regresa.
3. Si ninguno de estos está definido, vamos a la red.


```js
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v1");
  await cache.addAll(resources);
};

const putInCache = async (request, response) => {
  const cache = await caches.open("v1");
  await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
  // Primero intenta obtener el recurso desde caché
  const responseFromCache = await caches.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }

  // A continuación, intenta usar (y almacenar en caché) la respuesta precargada, si está allí
  const preloadResponse = await preloadResponsePromise;
  if (preloadResponse) {
    console.info("using preload response", preloadResponse);
    putInCache(request, preloadResponse.clone());
    return preloadResponse;
  }

  // A continuación, intenta obtener el recurso desde la red
  try {
    const responseFromNetwork = await fetch(request);
    // la respuesta solo se puede usar una vez
    // necesitamos guardar el clon para poner una copia en caché
    // y servir el segundo
    putInCache(request, responseFromNetwork.clone());
    return responseFromNetwork;
  } catch (error) {
    const fallbackResponse = await caches.match(fallbackUrl);
    if (fallbackResponse) {
      return fallbackResponse;
    }
    // cuando incluso la respuesta alternativa no está disponible,
    // no hay nada que podamos hacer, pero siempre debemos
    // devolver un objeto Response
    return new Response("Ocurrió un error de red", {
      status: 408,
      headers: { "Content-Type": "text/plain" },
    });
  }
};

// Habilita la precarga de navegación
const enableNavigationPreload = async () => {
  if (self.registration.navigationPreload) {
    // ¡Habilitar precargas de navegación!
    await self.registration.navigationPreload.enable();
  }
};

self.addEventListener("activate", (event) => {
  event.waitUntil(enableNavigationPreload());
});

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",
      "/star-wars-logo.jpg",
      "/gallery/bountyHunters.jpg",
      "/gallery/myLittleVader.jpg",
      "/gallery/snowTroopers.jpg",
    ]),
  );
});

self.addEventListener("fetch", (event) => {
  event.respondWith(
    cacheFirst({
      request: event.request,
      preloadResponsePromise: event.preloadResponse,
      fallbackUrl: "/gallery/myLittleVader.jpg",
    }),
  );
});
```


 Ten en cuenta que en este ejemplo descargamos y almacenamos en caché los mismos datos para el recurso, ya sea que se descargue "normalmente" o se precargue.
 En su lugar, puedes optar por descargar y almacenar en caché un recurso diferente en la precarga.
 Para obtener más información, consulta NavigationPreloadManager > Respuestas personalizadas.

Actualizar tu service worker
----------------------------

Si tu *service worker* se instaló anteriormente, pero luego está disponible una nueva versión del trabajador al actualizar o cargar la página, la nueva versión se instala en segundo plano, pero aún no está activada. Solo se activa cuando ya no hay páginas cargadas que todavía estén usando el antiguo *service worker*. Tan pronto como no queden más páginas cargadas, se activa el nuevo *service worker*.

Querrás actualizar tu escucha de eventos `install` en el nuevo *service worker* a algo como esto (observa el nuevo número de versión):


```js
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v2");
  await cache.addAll(resources);
};

self.addEventListener("install", (event) => {
  event.waitUntil(
    addResourcesToCache([
      "/",
      "/index.html",
      "/style.css",
      "/app.js",
      "/image-list.js",

      // ...

      // incluir otros nuevos recursos para la nueva versión…
    ]),
  );
});
```


Mientras esto sucede, la versión anterior sigue siendo responsable de las recuperaciones. La nueva versión se está instalando en segundo plano. Estamos llamando al nuevo caché `v2`, por lo que el caché anterior `v1` no se ve afectado.

Cuando ninguna página está usando la versión actual, el nuevo trabajador se activa y se vuelve responsable de las recuperaciones.

### Eliminar cachés antiguos

También obtienes un evento `activate`. Esto generalmente se usa para hacer cosas que habrían roto la versión anterior mientras aún se estaba ejecutando, por ejemplo, deshacerse de los cachés antiguos. Esto también es útil para eliminar datos que ya no se necesitan para evitar llenar demasiado espacio en disco: cada navegador tiene un límite estricto en la cantidad de almacenamiento en caché que puede usar un determinado *service worker*. El navegador hace todo lo posible para administrar el espacio en disco, pero puede eliminar el almacenamiento en caché de un origen. El navegador, generalmente, eliminará todos los datos de un origen o ninguno de los datos de un origen.

Las promesas pasadas a `waitUntil()` bloquearán otros eventos hasta que se completen, por lo que puedes estar seguro de que tu operación de limpieza se habrá completado cuando obtengas tu primer evento `fetch` en el nuevo *service worker*.


```js
const deleteCache = async (key) => {
  await caches.delete(key);
};

const deleteOldCaches = async () => {
  const cacheKeepList = ["v2"];
  const keyList = await caches.keys();
  const cachesToDelete = keyList.filter((key) => !cacheKeepList.includes(key));
  await Promise.all(cachesToDelete.map(deleteCache));
};

self.addEventListener("activate", (event) => {
  event.waitUntil(deleteOldCaches());
});
```

Herramientas de desarrollo
--------------------------

Chrome tiene `chrome://inspect/#service-workers`, que muestra la actividad actual de los *service workers* y el almacenamiento en un dispositivo, y `chrome://serviceworker-internals`, que muestra más detalles y te permite iniciar/detener/depurar el proceso del trabajador. En el futuro, tendrán modos de limitación/desconexión para simular conexiones defectuosas o inexistentes, lo que será algo realmente bueno.

Firefox también ha comenzado a implementar algunas herramientas útiles relacionadas con los *service workers*:

* Puedes navegar a `about:debugging` para ver qué SWs están registrados y actualizarlos/eliminarlos.
* Al realizar pruebas, puedes sortear la restricción de HTTPS marcando la opción "Habilitar *service worker* a través de HTTP (cuando la caja de herramientas está abierta)" en la Configuración de herramientas de desarrollo de Firefox.
* El botón "Olvidar", disponible en las opciones de personalización de Firefox, se puede usar para borrar los *service workers* y sus cachés (Error 1252998 en Firefox).

**Nota:** Puedes servir tu aplicación desde `http://localhost` (por ejemplo, usando `me@localhost:/my/app$ python -m SimpleHTTPServer`) para el desarrollo local. Ve Consideraciones de seguridad

Véase también
-------------

* El manual del *service worker*
* ¿Está listo ServiceWorker?
* Descarga la hoja de trucos de *service worker* 101.
* Promesas
* Usar *web workers*