Skip to main content

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