Capítulo 4 - Conceptos básicos sobre Service Workers

Continuamos con PWA. Creo que ya tenemos una idea de lo que nos enfrentamos en esta serie, por tanto, centrémonos en apartados más técnicos para poder cumplir con los objetivos que Google nos aconseja tener.

Durante algunas semanas nos centraremos en una de las API que quieren revolucionar la forma en la que entendemos el front y en cómo gestionamos y administramos los recursos de nuestro navegador: Los Workers.

En nuestro caso los Workers que más nos interesan son los Service Workers. En el posts de hoy, aprenderemos a diferenciar los tipos de Workers que existen, en qué nos puede ayudar cada uno y cómo les podemos sacar provecho para crear aplicaciones Web que le den guerra a las aplicaciones nativas.

¿Qué son?

Como ya sabemos - y si no, te lo cuento ahora - JavaScript es un lenguaje monohilo. Esto quiere decir que toda la interpretación del código de nuestra aplicación va a ser atendido simplemente en un único hilo de ejecución.

Su forma de realizar diferente acciones es por medio del uso de la entrada y salida de manera asíncrona y controlando todas las acciones por medio de un bucle que va decidiendo que se tiene que ir haciendo en cada momento.

Claro, esto en aplicaciones simples, no supone ningún problema, pero ¿qué ocurre si necesitamos ejecutar una Web que requiere mucho recursos de máquina? Pues que el sistema de único hilo no funciona.

Pongámonos en el caso de uso en la que nuestro navegador tiene que renderizar muchas animaciones sin que estas sufran de ningún tipo de lag ni de parón incomodo para el ojo humano. Imaginemos que a la vez que mostramos la animación, se necesita realizar un procesado de datos por medio de JavaScript.

Bueno, puede ocurrirnos algo como esto:

Es lo que os comentaba. Esto es una animación de kirby que en cuanto ejecutamos un algoritmo para calcular muchos número aleatorios se queda congelada. Este lag se debe a que JavaScript ejecuta todo en un solo hilo de ejecución y la animación tiene que parar para dejar paso al cómputo del algoritmo.

Bloquear animaciones es una mala idea si queremos ser competitivos en el mundo móvil. Conseguir que el navegador reproduzca 60 frames por segundo (que es lo que el ojo humano sabe interpretar como un movimiento real) es uno de nuestros trabajos.

Para ello vamos a tener que trabajar con nuevas herramientas que nos lo permitan. JavaScript por si mismo no nos va a poder ayudar por su propia naturaleza. ¿Entonces?

En otros lenguajes de programación la ejecución multihilo, la posibilidad de lanzar ejecuciones concurrentes es algo asimilado desde hace décadas y supuso un avance en el procesamiento de información. Java, C# son lenguajes que ya soportan la gestión de recursos en paralelos.

En JavaScript se ha implementado lo que se conoce como Workers, que no son otra cosa que la posibilidad de ejecutar tareas en otro hilos de ejecución. El poder paralelizar tareas nos ayuda en muchas cosas por que es una buena solución para lanzar tareas en segundo plano. No todo el procesamiento que hace una aplicación tiene que ver con lo que mostramos al usuario.

Por tanto, podemos estar:

  • Monitorizando muchas cosas sin que el usuario se de cuenta,
  • Descargando información que el usuario usará más tarde,
  • Intentando sincronizar información del usuario con nuestro servidor que en otro momento fue imposible debido a la conexión de datos.

Estas situaciones son resueltas con dos tipos diferentes de Workers:

Puede que volvamos más adelante a los Web Workers, pero detengámonos en los Service Workers debido a su gran potencial de comunicación y no solo a su labor de paralelización de recursos.

¿Para qué pueden usarse?

Los Service Workers nos interesan en el desarrollo de una PWA por 4 utilidades favorecedoras para nosotros:

  1. Nos permiten realizar tareas de cacheo de información . Puedo estar descargando vistas y datos en segundo plano de nuestros servidores sin que el usuario sea consciente. Este cacheo puede ayudar a la fluidez de la aplicación y a seguir el patrón PRPL que comentamos en el post anterior.
  2. Nos permite tener un sistema por donde recibir notificaciones Push . Abrir un canal de comunicación con un servidor y que el propio servidor sepa enviarnos información de forma proactiva. Podemos dejar estas tareas en un segundo plano sin entorpecer en el hilo principal.
  3. Nos permite sincronizar datos en segundo plano. Puede que la conexión se haya caído y que tengamos datos no sincronizados en servidor. En vez de dejar al usuario colgado, almacenamos y enviamos cuando sea posible.
  4. Permitir instalar y actualizar nuestra aplicación . Podemos dejar un hilo secundario comprobando cuando hay cambios en la aplicación y que esta los instale cuando los haya.

Son 4 tareas que premian el trabajo offline, la velocidad del sistema en términos generales y el ahorro de recursos. Justamente lo que una buena PWA nos demanda. Por tanto, los Service Workers son uno de los pilares en los que una aplicación Web del futuro se sustenta.

¿Qué tengo que tener en cuenta cuando los use?

Ahora bien, los Service Workers no son el maná ni son algo tan del presente como podamos pensar. Detengámonos un rato por ejemplo en qué cosas tenemos que tener en cuenta si queremos hacer uso de ellos:

  • Un Service Worker se ejecuta en otro contexto diferente al del hilo principal. Esto parece obvio, pero una de las primeras consecuencias es que desde él no tenemos acceso al DOM. Al DOM solo se puede acceder desde el hilo principal. Podemos comunicarnos con el hilo principal y que el ejecute acciones por nosotros, pero ya tenemos que programar esa sincronización.
  • Un Service Worker está pensado para el cómputo asíncrono donde no existe el bloqueo del proceso. Por tanto, ten en cuenta qué partes de la API de JavaScript son síncronas porque no podrás hacer uso de ellas en un Service Worker. Por ejemplo el uso de localStorage y el uso de XHR de manera síncronas dentro de un Service Worker no son posibles porque bloquearías el proceso cuando es lo que intentamos evitar.
  • Un Service Worker trata de levantar un nuevo contexto y tiene control total sobre las comunicaciones que tiene el sistema. Esto puede traer a la larga vulnerabilidades así que un Service Worker siempre tiene que ser ejecutado sobre redes HTTPS para evitar ataques Man In The Middle. Al obligar HTTPS siempre nos aseguramos que el Services Worker está cargado por nuestro servidor y no por terceras partes. Además, y como apunte, ten en cuenta que en Firefox los Service Workers no funcionan en modo privado o incógnito.

Por tanto y en resumen: el uso de un Service Worker es algo muy especifico y muchas acciones que tienen que ir en el hilo principal de ejecución, no tendrán cabida dentro de él, tenlo en cuenta.

Otro tema importante es cómo se encuentra la especificación de la API implementada en los navegadores. Veamos el CanIUse para Service Workers a día de hoy:

Tiene un progreso prometedor, pero todavía hay demasiados navegadores que pasan de ellos. Apple ha comentado que el año que viene entran dentro de su Roadmap, pero hasta entonces recuerda que hacer una PWA para un dispositivo iOS es algo que no es posible. Y esto es importante porque puede que a negocio le eche para atrás aventurarse en el desarrollo de una PWA si su target va a verse reducido.

Por tanto, cuidado. Incluyamos Service Workers si pueden ayudarnos a hacer mejores aplicaciones, pero tengamos en cuenta todo lo anterior. En estos primeros años, hasta que los Service Workers se normalicen en todos los navegadores, programemos de manera defensiva y progresiva, comprobemos en todo momento si la funcionalidad está disponible para evitar fallos innecesarios.

Registrando un Service Worker

No quiero dejar el posts sin incluir algo práctico, pero como imagino que ya tenemos bastante información por hoy, vamos a centrarnos simplemente en cómo debemos registrar un Service Worker en nuestro sistema. Como veremos es un método bastante desacoplado que piensa en todos los problemas que nos podamos encontrar en dispositivos legacy.

Lo primero que tenemos que tener en cuenta es desde dónde queremos que tenga acceso el Service Worker. No es lo mismo registrar un Service Worker que se encuentra en la raíz que un Service Worker que se encuentre en una ruta interna. Es decir, que si tu registras un Service Worker que tengas en la raíz de tu proyecto, tendrás acceso a todos los recursos desde la raíz.

Pero que si tu Service Worker se encuentra en /example solo tendrá acceso a recursos de red sobre esta URL. Lo que provoca que si tengo un fichero app.css solo pueda trabajar con él si se encuentra en /example/app.css. Nuestro SW se encontrará en la raíz y se llamará sw.js.

Una vez que tenemos claro esto, programamos nuestro register. Lo primero que vamos a hacer es curarnos de salud y comprobar que el navegador desde donde se ejecuta nuestra aplicación tiene disponible la API de Service Worker.

Esto se consigue así:

if ('serviceWorker' in navigator) {
   // código
}

Una vez que vemos que efectivamente tenemos disponible la API, podemos registrar el SW hospedado en nuestro servidor. Ahora bien, detengámonos un momentos. Registrar e instalar el SW nos llevará un tiempo de cómputo que asume el hilo principal. Por tanto, puede que reduzcamos el rendimiento de nuestra aplicación. Como un SW es una funcionalidad por lo general añadida, pero que no es crítica, vamos a esperar a registrarlo una vez que todos los recursos críticos hayan sido cargados.

Para ello, usemos el evento load para estar seguros que solo se registrará cuanto todo esté listo:

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
        // código
    }
}

Cuidado con esto, porque dependiendo del tipo de aplicación que desarrolles, puede que esta espera no sea suficiente todavía. Imagina que nada mas empezar tu aplicación, se va a ejecutar una animación o que tu framework favorito necesita un tiempo para configurar todo lo necesario para funcionar.

Puede que el registro tenga que posponerse un poco más, así que recuerda posponer el registro lo máximo que puedas hasta que estés seguro que el sistema se encontrará en cierto reposo y que no mines la experiencia de usuario.

Lo ultimo ya es hacer el registro:

if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
        navigator.serviceWorker.register('/sw.js')
          .then((registration) => console.log(registrarion.scope))
          .catch(error) => console.error(error)); 
    }
}

Y con eso, el navegador sabe como instalar el Worker y ejecutar lo que hayamos programado en sw.js.

Puedes llamar a register() sin ningún problema cada vez que cargues la página. El navegador determinará si el Service Worker está registrado o no, y actuará según corresponda.

En próximos capítulos, estudiaremos toda la API y explicaremos cómo sacarle partido. Por ahora es todo!

Nos leemos :)

Recursos utilizados para este capítulo:

results matching ""

    No results matching ""