Saltar al contenido principal




Ahora es más fácil mover el trabajo pesado a subprocesos en segundo plano con los módulos de JavaScript en los trabajadores web.

JavaScript es de un solo subproceso, lo que significa que solo puede realizar una operación a la vez. Esto es intuitivo y funciona bien para muchos casos en la web, pero puede resultar problemático cuando necesitamos realizar tareas pesadas como procesamiento de datos, análisis sintáctico, cálculo o análisis. A medida que se entregan aplicaciones cada vez más complejas en la web, existe una mayor necesidad de procesamiento de subprocesos múltiples.

En la plataforma web, la primitiva principal para el enhebrado y el paralelismo es el API de trabajadores web. Los trabajadores son una abstracción ligera además de subprocesos del sistema operativo que exponen un mensaje que pasa API para la comunicación entre subprocesos. Esto puede ser inmensamente útil cuando se realizan cálculos costosos o se opera en grandes conjuntos de datos, lo que permite que el hilo principal se ejecute sin problemas mientras se realizan las costosas operaciones en uno o más hilos en segundo plano.

Aquí hay un ejemplo típico de uso de trabajador, donde un script de trabajador escucha mensajes del hilo principal y responde enviando mensajes propios:

page.js:

const worker = new Worker('worker.js');
worker.addEventListener(e => {
console.log(e.data);
});
worker.postMessage('hello');

worker.js:

addEventListener('message', e => {
if (e.data === 'hello') {
postMessage('world');
}
});

La API de Web Worker ha estado disponible en la mayoría de los navegadores durante más de diez años. Si bien eso significa que los trabajadores tienen un excelente soporte de navegador y están bien optimizados, también significa que son anteriores a los módulos de JavaScript. Dado que no existía un sistema de módulos cuando se diseñaron los trabajadores, la API para cargar código en un trabajador y componer scripts se ha mantenido similar a los enfoques de carga de scripts síncronos comunes en 2009.

Historia: trabajadores clásicos

El constructor Worker toma un guión clásico URL, que es relativa a la URL del documento. Inmediatamente devuelve una referencia a la nueva instancia de trabajador, que expone una interfaz de mensajería, así como una terminate() método que detiene y destruye inmediatamente al trabajador.

const worker = new Worker('worker.js');

Un importScripts() La función está disponible dentro de los trabajadores web para cargar código adicional, pero detiene la ejecución del trabajador para buscar y evaluar cada script. También ejecuta scripts en el ámbito global como un clásico. <script> , lo que significa que las variables de un script pueden ser sobrescritas por las variables de otro.

worker.js:

importScripts('greet.js');
addEventListener('message', e => {
postMessage(sayHello());
});

saludar.js:


function sayHello() {
return 'world';
}

Por esta razón, los trabajadores web históricamente han impuesto un efecto descomunal en la arquitectura de una aplicación. Los desarrolladores han tenido que crear herramientas inteligentes y soluciones para hacer posible el uso de trabajadores web sin renunciar a las prácticas de desarrollo modernas. Como ejemplo, los paquetes como webpack incorporan una implementación de cargador de módulo pequeño en el código generado que usa importScripts()
para la carga de código, pero envuelve los módulos en funciones para evitar colisiones de variables y simular importaciones y exportaciones de dependencias.

Ingresar trabajadores del módulo

Un nuevo modo para los trabajadores web con los beneficios de ergonomía y rendimiento de Módulos JavaScript se envía en Chrome 80, denominados trabajadores del módulo. los
Worker constructor ahora acepta un nuevo {type:"module"} opción, que cambia la carga y ejecución del script para que coincida <script type="module">.

const worker = new Worker('worker.js', {
type: 'module'
});

Dado que los trabajadores de módulo son módulos de JavaScript estándar, pueden utilizar declaraciones de importación y exportación. Al igual que con todos los módulos de JavaScript, las dependencias solo se ejecutan una vez en un contexto dado (hilo principal, trabajador, etc.), y todas las importaciones futuras hacen referencia a la instancia del módulo ya ejecutada. Los navegadores también optimizan la carga y ejecución de módulos JavaScript. Las dependencias de un módulo se pueden cargar antes de que se ejecute el módulo, lo que permite cargar árboles de módulos completos en paralelo. La carga de módulos también almacena en caché el código analizado, lo que significa que los módulos que se utilizan en el hilo principal y en un trabajador solo necesitan analizarse una vez.

Pasar a módulos de JavaScript también permite el uso de importación dinámica para código de carga diferida sin bloquear la ejecución del trabajador. La importación dinámica es mucho más explícita que usar importScripts() para cargar dependencias, ya que las exportaciones del módulo importado se devuelven en lugar de depender de variables globales.

worker.js:

import { sayHello } from './greet.js';
addEventListener('message', e => {
postMessage(sayHello());
});

saludar.js:

import greetings from './data.js';
export function sayHello() {
return greetings.hello;
}

Para garantizar un gran rendimiento, el viejo importScripts() El método no está disponible en los trabajadores del módulo. Cambiar trabajadores para usar módulos de JavaScript significa que todo el código se carga en Modo estricto. Otro cambio notable es que el valor de this en el ámbito de nivel superior de un módulo JavaScript es
undefined, mientras que en los trabajadores clásicos el valor es el alcance global del trabajador. Afortunadamente, siempre ha habido una self global que proporciona una referencia al alcance global. Está disponible en todo tipo de trabajadores, incluidos los trabajadores de servicios, así como en el DOM.

Los trabajadores del módulo también eliminan la compatibilidad con comentarios de estilo HTML. ¿Sabía que puede utilizar comentarios HTML en scripts de trabajadores web?

Precargar trabajadores con modulepreload

Una mejora sustancial del rendimiento que viene con los trabajadores del módulo es la capacidad de precargar trabajadores y sus dependencias. Con los trabajadores del módulo, los scripts se cargan y ejecutan como módulos estándar de JavaScript, lo que significa que pueden precargarse e incluso pre-analizarse usando modulepreload:


<link rel="modulepreload" href="worker.js">

<script>
addEventListener('load', () => {
const worker = new Worker('worker.js', { type: 'module' });
});
</script>

Los módulos precargados también pueden ser utilizados tanto por el hilo principal como por los trabajadores del módulo. Esto es útil para módulos que se importan en ambos contextos, o en casos en los que no es posible saber de antemano si un módulo se utilizará en el hilo principal o en un trabajador.

Anteriormente, las opciones disponibles para precargar scripts de trabajadores web eran limitadas y no necesariamente confiables. Los trabajadores clásicos tenían su propio tipo de recurso «trabajador» para la precarga, pero no se implementaron navegadores <link rel="preload" as="worker">. Como resultado, la técnica principal disponible para precargar los trabajadores web era utilizar <link rel="prefetch">, que se basó completamente en la caché HTTP. Cuando se usa en combinación con los encabezados de almacenamiento en caché correctos, esto hizo posible evitar que la instanciación del trabajador tuviera que esperar para descargar el script del trabajador. Sin embargo, a diferencia de
modulepreload esta técnica no admitía la carga previa de dependencias ni el análisis previo.

¿Qué pasa con los trabajadores compartidos?

Trabajadores compartidos se han actualizado con soporte para módulos JavaScript a partir de Chrome 83. Al igual que los trabajadores dedicados, la construcción de un trabajador compartido con el {type:"module"} La opción ahora carga el script de trabajo como un módulo en lugar de un script clásico:

const worker = new SharedWorker('/worker.js', {
type: 'module'
});

Antes de la compatibilidad con los módulos de JavaScript, SharedWorker() El constructor esperaba solo una URL y una opción name argumento. Esto seguirá funcionando para el uso clásico de trabajadores compartidos; sin embargo, la creación de módulos de trabajadores compartidos requiere el uso de la nueva options argumento. los Opciones Disponibles
son los mismos que los de un trabajador dedicado, incluido el name opción que reemplaza a la anterior name argumento.

¿Qué pasa con el trabajador de servicios?

La especificación del trabajador de servicio ya ha sido actualizado para admitir la aceptación de un módulo JavaScript como punto de entrada, utilizando el mismo {type:"module"} opción como trabajadores del módulo, sin embargo, este cambio aún no se ha implementado en los navegadores. Una vez que eso suceda, será posible crear una instancia de un trabajador de servicio usando un módulo de JavaScript usando el siguiente código:

navigator.serviceWorker.register('/sw.js', {
type: 'module'
});

Ahora que se actualizó la especificación, los navegadores están comenzando a implementar el nuevo comportamiento. Esto lleva tiempo porque existen algunas complicaciones adicionales asociadas con la incorporación de módulos de JavaScript al trabajador del servicio. El registro del trabajador de servicio debe comparar los scripts importados con sus versiones anteriores en caché al determinar si se debe activar una actualización, y esto debe implementarse para los módulos de JavaScript cuando se utilizan para los trabajadores del servicio. Además, los trabajadores de servicios deben poder omitir el caché para scripts en ciertos casos al buscar actualizaciones.

Recursos adicionales y lectura adicional

R Marketing Digital