Los trabajadores de servicio son poderosos y vale la pena aprenderlos. Le permiten ofrecer un nivel de experiencia totalmente nuevo a sus usuarios. Su sitio puede cargar al instante. Puede funcionar sin conexión. Puede instalarse como una aplicación nativa y sentirse igual de pulido, pero con el alcance y la libertad de la web.

Pero los service workers no se parecen a nada a lo que la mayoría de los desarrolladores web estamos acostumbrados. Tienen una curva de aprendizaje muy pronunciada y un puñado de inconvenientes con los que hay que tener cuidado.

Los desarrolladores de Google y yo colaboramos recientemente en un proyecto, Service Workies, un juego gratuito para entender los service workers. Mientras lo construía y trabajaba con los complejos entresijos de los trabajadores de servicios, me encontré con algunos inconvenientes. Lo que más me ayudó fue dar con un puñado de metáforas descriptivas. En este post vamos a explorar estos modelos mentales y envolver nuestros cerebros en torno a los rasgos paradójicos que hacen que los trabajadores de servicio sean a la vez difíciles e impresionantes.

Lo mismo, pero diferente #

Mientras codificas tu trabajador de servicio, muchas cosas te resultarán familiares. Tienes la oportunidad de utilizar tus nuevas características favoritas del lenguaje JavaScript. Usted escucha a los eventos del ciclo de vida al igual que con los eventos de la interfaz de usuario. Gestionas el flujo de control con promesas como estás acostumbrado.

Pero otros comportamientos del service worker te hacen rascarte la cabeza con confusión. Especialmente cuando refrescas la página y no ves los cambios en el código aplicados.

Una nueva capa #

Normalmente cuando construyes un sitio tienes sólo dos capas en las que pensar: el cliente y el servidor. El trabajador de servicio es una nueva capa que se encuentra en el medio.

Piense en su trabajador de servicio como una especie de extensión del navegador-uno que su sitio puede instalar en el navegador de su usuario. Una vez instalado, el service worker amplía el navegador de su sitio con una potente capa intermedia. Esta capa de trabajador de servicios puede interceptar y manejar todas las solicitudes que su sitio hace.

La capa de trabajador de servicios tiene su propio ciclo de vida independiente de la pestaña del navegador. Una simple actualización de la página no es suficiente para actualizar un trabajador de servicio, al igual que no se espera que una actualización de la página actualice el código desplegado en un servidor. Cada capa tiene sus propias reglas únicas para la actualización.

En el juego Service Workies cubrimos los muchos detalles del ciclo de vida del trabajador de servicio y le damos una tonelada de práctica trabajando con él.

Piense en su trabajador de servicio como una nueva capa intermedia con su propio ciclo de vida y métodos para la actualización.

Potente, pero limitado #

Tener un trabajador de servicio en su sitio le da increíbles beneficios. Su sitio puede:

  • funcionar sin problemas incluso cuando el usuario está desconectado
  • obtener mejoras masivas de rendimiento a través del almacenamiento en caché
  • utilizar notificaciones push
  • instalarse como una PWA

Con todo lo que pueden hacer los service workers, están limitados por diseño. No pueden hacer nada sincrónico o en el mismo hilo que tu sitio. Eso significa que no pueden acceder a:

  • almacenamiento local
  • el DOM
  • la ventana

La buena noticia es que hay un puñado de formas en las que tu página puede comunicarse con su trabajador de servicio, incluyendo canales de mensajes directos postMessage, uno-a-uno y canales de difusión uno-a-muchos.

Piensa en tu trabajador de servicio como algo que vive fuera de tu página. Usted puede hablar con él, pero no puede acceder a su página directamente.

De larga duración, pero de corta duración #

Un service worker activo sigue viviendo incluso después de que un usuario abandone su sitio o cierre la pestaña. El navegador mantiene este trabajador de servicio para que esté listo la próxima vez que el usuario regrese a su sitio. Antes de que se haga la primera petición, el trabajador de servicio tiene la oportunidad de interceptarla y tomar el control de la página. Esto es lo que permite que un sitio funcione fuera de línea -el service worker puede servir una versión en caché de la propia página, incluso si el usuario no tiene conexión a Internet.

En Service Workies visualizamos este concepto con Kolohe (un simpático service worker) interceptando y manejando las peticiones.

Detenido #

A pesar de que los service workers parecen ser inmortales, pueden ser detenidos en casi cualquier momento. El navegador no quiere desperdiciar recursos en un service worker que no está haciendo nada en ese momento. Detenerse no es lo mismo que terminarse: el service worker sigue instalado y activado. Simplemente se pone a dormir. La próxima vez que se necesita (por ejemplo, para manejar una solicitud), el navegador lo despierta de nuevo.

waitUntil #

Debido a la constante posibilidad de ser puesto a dormir, su trabajador de servicio necesita una manera de hacer saber al navegador cuando está haciendo algo importante y no tiene ganas de tomar una siesta. Aquí es donde event.waitUntil() entra en juego. Este método extiende el ciclo de vida en el que se utiliza, evitando que se detenga y que pase a la siguiente fase de su ciclo de vida hasta que estemos preparados. Esto nos da tiempo para configurar cachés, obtener recursos de la red, etc.

Este ejemplo le dice al navegador que nuestro service worker no ha terminado de instalarse hasta que la caché assets haya sido creada y poblada con la imagen de una espada:

self.addEventListener("install", event => {
event.waitUntil(
caches.open("assets").then(cache => {
return cache.addAll();
})
);
});

Cuidado con el estado global #

Cuando este inicio/parada ocurre el ámbito global del service worker se restablece. Así que ten cuidado de no utilizar ningún estado global en tu service worker o estarás triste la próxima vez que se despierte y tenga un estado diferente al que esperaba.

Considera este ejemplo que utiliza un estado global:

const favoriteNumber = Math.random();
let hasHandledARequest = false;
self.addEventListener("fetch", event => {
console.log(favoriteNumber);
console.log(hasHandledARequest);
hasHandledARequest = true;
});

En cada petición este service worker registrará un número-digamos 0.13981866382421893. La variable hasHandledARequest también cambia a true. Ahora el service worker se queda inactivo durante un rato, por lo que el navegador lo detiene. La próxima vez que haya una solicitud, el trabajador de servicio es necesario de nuevo, por lo que el navegador lo despierta. Su script se evalúa de nuevo. Ahora hasHandledARequest se restablece a false, y favoriteNumber es algo completamente diferente-0.5907281835659033.

No puedes confiar en el estado almacenado en un service worker. Además, la creación de instancias de cosas como los canales de mensajes puede causar errores: obtendrás una nueva instancia cada vez que el trabajador de servicio se detenga/arranque.

Atención: Este inconveniente es especialmente importante tenerlo en cuenta mientras trabajas en el código de tu service worker porque cuando Chrome DevTools está abierto, el comportamiento de inicio/parada está desactivado. Puede que ni siquiera veas los errores causados por depender del estado global hasta que hayan sido enviados a tus usuarios.

En el capítulo 3 de Service Workies visualizamos nuestro service worker detenido como si perdiera todo el color mientras espera a ser despertado.

Piensa en tu service worker como en un perro de raza whippet. Es rápido, leal e increíble. Se quedará a tu lado pase lo que pase. Pero sobre todo quiere dormir. Todo el tiempo. Tienes que hacerle saber cuando quieres que se quede despierto. Buen perro!

Juntos, pero separados #

Su página sólo puede ser controlada por un service worker a la vez. Pero puede tener dos service workers instalados a la vez. Cuando usted hace un cambio en el código de su trabajador de servicio y actualiza la página, en realidad no está editando su trabajador de servicio en absoluto. Los service workers son inmutables. En su lugar, estás creando uno nuevo. Este nuevo service worker (llamémoslo SW2) se instalará pero no se activará todavía. Tiene que esperar a que el service worker actual (SW1) termine (cuando su usuario abandone su sitio).

Mezclando con las cachés de otro service worker #

Mientras se instala, el SW2 puede configurar cosas – normalmente creando y poblando cachés. Pero atención: este nuevo service worker tiene acceso a todo lo que el service worker actual tiene acceso. Si no tiene cuidado, su nuevo trabajador de servicio en espera puede realmente estropear las cosas para su trabajador de servicio actual. Algunos ejemplos que podrían causarle problemas:

  • SW2 podría eliminar una caché que SW1 está utilizando activamente.
  • SW2 podría editar el contenido de una caché que SW1 está utilizando, haciendo que SW1 responda con activos que la página no está esperando.

Skip skipWaiting #

Un service worker también puede utilizar el arriesgado método skipWaiting() para tomar el control de la página tan pronto como termine de instalarse. Esto es generalmente una mala idea a menos que usted está intencionalmente tratando de reemplazar un trabajador de servicio buggy. El nuevo trabajador de servicio podría estar utilizando recursos actualizados que la página actual no está esperando, dando lugar a errores y bugs.

Empezar limpio #

La forma de evitar que sus trabajadores de servicio se empalmen entre sí es asegurarse de que utilizan diferentes cachés. La forma más fácil de conseguirlo es versionar los nombres de las cachés que utilizan.

const version = 1;
const assetCacheName = `assets-${version}`;
self.addEventListener("install", event => {
caches.open(assetCacheName).then(cache => {
// confidently do stuff with your very own cache
});
});

Cuando despliegues un nuevo service worker, bumpearás el version para que haga lo que necesita con una caché completamente distinta a la del service worker anterior.

Finalizar clean #

Una vez que tu service worker alcance el estado activated, sabrás que ha tomado el control, y que el service worker anterior es redundante (es decir, ya no es necesario). En este punto es importante limpiar el antiguo service worker. No sólo respeta los límites de almacenamiento de la caché de sus usuarios, sino que también puede evitar errores involuntarios.

El método caches.match() es un atajo utilizado a menudo para recuperar un elemento de cualquier caché donde haya una coincidencia. Pero itera a través de las cachés en el orden en que fueron creadas. Digamos que tiene dos versiones de un archivo de script app.js en dos cachés diferentes-assets-1 y assets-2. Tu página espera el script más nuevo que está almacenado en assets-2. Pero si no has borrado la caché antigua, caches.match('app.js') va a devolver el antiguo de assets-1 y muy probablemente romperá tu sitio.

Todo lo que se necesita para limpiar después de los trabajadores de servicio anteriores es borrar cualquier caché que el nuevo trabajador de servicio no necesite:

const version = 2;
const assetCacheName = `assets-${version}`;
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== assetCacheName){
return caches.delete(cacheName);
}
});
);
});
);
});

Evitar que sus trabajadores de servicio se peguen unos a otros requiere un poco de trabajo y disciplina, pero vale la pena.

Piense en la combinación de su trabajador de servicio y su sitio como una aplicación instalable. Cada versión debería funcionar. Cada versión debería estar separada de las demás. Imagínese cómo sería de buggy un juego si el desarrollador accidentalmente lanzó un parche que utiliza la nueva lógica del juego, pero los activos obsoletos. ¡Te enfurecerías en los foros tan rápido! Mantén las versiones de tu aplicación ordenadas &limpias.

La mentalidad del trabajador de servicio #

Conseguir la mentalidad correcta al pensar en los trabajadores de servicio te ayudará a construir el tuyo con confianza. Una vez que les cojas el tranquillo podrás crear experiencias increíbles para tus usuarios.

Si quieres entender todo esto jugando a un juego, ¡estás de suerte! Ve a jugar a Service Workies, donde aprenderás los caminos del trabajador de servicios para poder matar a las bestias offline.

Última actualización: 4 de junio de 2019 Mejorar el artículo

Deja una respuesta

Tu dirección de correo electrónico no será publicada.