Skip to content
Aprende a hacer apps móviles con Ionic 2 

Angular con WebWorkers: paso a paso

Aprovechando que di una charla en los Angular Community Days para explicar el potencial de usar WebWorkers con Angular, voy a dejarte esta guía paso a paso de cómo modificar una aplicación de Angular (v2 o v4) para sacar provecho de esta killer feature.

Si quieres una introducción a los web workers, te recomiendo que le des un vistazo a la presentación. Pero si quieres saltarte la intro e ir al grano, te lo resumo:

Angular te permite ejecutar la lógica de tu aplicación en un segundo thread para liberar tu UI, mejorando la experiencia de usuario.

Angular te permite ejecutar toda la lógica de tu aplicación en un segundo thread (WebWorker), de modo que libera absolutamente a tu UI, evitando que vaya a trompicones cuando estas haciendo un uso exhaustivo de CPU en el código.

Puedes ver por ti mismo los resultados en la demo online del código que utilicé para este otro artículo sobre WebWorkers en Angular.

angular webworkers demo
Spoiler de la demo

Precondiciones

Estoy asumiendo que tienes un proyecto en Angular (v2 o v4) generado con la CLI de Angular v1.0 o superior.

Esto último no es imprescindible, pero las modificaciones del archivo de webpack las haré basándome en la configuración que genera la CLI.

Instrucciones

Extraer el archivo de webpack

Desde Angular CLI v1.0, existe el comando eject que permite extraer el archivo de configuración de webpack y manipularlo a tu antojo.

  1. Ejecuta ng eject para que Angular CLI genere el archivo webpack.config.js.

  2. Ejecuta npm install para instalar las nuevas dependencias generadas por la CLI al extraer el archivo anterior.

Instala las dependencias de los webworkers

Para poder lanzar la app en un WebWorker, necesitas instalar algunas librerías de angular.

Ejecuta npm install --save @angular/platform-webworker @angular/platform-webworker-dynamic

Cambios en la inicialización del UI thread

####Cambios en app.module.ts
Reemplaza BrowserModule por WorkerAppModule en el archivo app.module.ts. También tienes que actualizar el import ya que usa la librería @angular/platform-webworker.

//src/app/app.module.ts

import { WorkerAppModule } from '@angular/platform-webworker';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
//...other imports...

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    WorkerAppModule,
    //...other modules...
  ],
  providers: [/*...providers...*/],
  bootstrap: [AppComponent]
})
export class AppModule { }

Cambios en src/main.ts

Reemplaza el proceso de bootstrap con bootstrapWorkerUI (actualiza también el import).

Tendrás que pasarle una URL al archivo donde tienes definido el WebWorker. Utiliza el nombre webworker.bundle.js, no te preocupes, lo creamos en un momento.

//main.ts

import { enableProdMode } from '@angular/core';
import { bootstrapWorkerUi } from '@angular/platform-webworker';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

bootstrapWorkerUi('webworker.bundle.js');

Crea el archivo workerLoader.ts

  1. Crea un nuevo archivo src/workerLoader.ts.

  2. Como tu WebWorker será un único archivo que contiene todas sus dependencias (vive en un proceso aparte al thread principal y por tanto no tiene acceso al vendor.js que tengas allí), necesitas incluir los paquetes polyfills.ts, @angular/core y @angular/common. Más adelante verás como actualizar Webpack para transpilar y generar el bundle con el resultado.

  3. Importa platformWorkerAppDynamic

  4. Importa AppModule (elimina su import de main.ts) y lánzalo a través de la plataforma platformWorkerAppDynamic.

//workerLoader.ts

import 'polyfills.ts';
import '@angular/core';
import '@angular/common';

import { platformWorkerAppDynamic } from '@angular/platform-webworker-dynamic';
import { AppModule } from './app/app.module';

    platformWorkerAppDynamic().bootstrapModule(AppModule);

Actualiza Webpack para generar tu WebWorker

El archivo auto-generado de Webpack es muy extenso, pero no te preocupes. Solo necesitas fijarte en estos puntos:

  1. Añade un entry point webworker para tu archivo workerLoader.ts. Si te fijas en la parte de output, verás que se añade el sufijo “bundle.js” a todos los chunks. Por eso, en la fase de bootstrap te he hecho usar el nombre webworker.bundle.js

  2. Ves a HtmlWebpackPlugin y excluye el entry point webworker, de modo que su archivo no se incluya en el index.html que se genera on the fly.

  3. Ves a CommonChunksPlugin y para el chunk inline, escribe explícitamente los chunks de entrada, para prevenir que se incluya el de webworker.

  4. Ves a AotPlugin y define el entryModule explícitamente.

A continuación tienes el resultado de los puntos modificados en el webpack config.

// webpack.config.js

//...some stuff...
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CommonsChunkPlugin } = require('webpack').optimize;
const { AotPlugin } = require('@ngtools/webpack');
//...some stuff...

module.exports = {
  //...some stuff...
  "entry": {
    "main": [
      "./src/main.ts"
    ],
    "polyfills": [
      "./src/polyfills.ts"
    ],
    "styles": [
      "./src/styles.css"
    ],
    "webworker": [
      "./src/workerLoader.ts"
    ]
  },
  "output": {
    "path": path.join(process.cwd(), "dist"),
    "filename": "[name].bundle.js",
    "chunkFilename": "[id].chunk.js"
  },
  "module": { /*...a lot of stuff...*/ },
  "plugins": [
    //...some stuff...
    new HtmlWebpackPlugin({
      //...some stuff...
      "excludeChunks": [
        "webworker"
      ],
      //...some more stuff...
    }),
    new BaseHrefWebpackPlugin({}),
    new CommonsChunkPlugin({
      "name": "inline",
      "minChunks": null,
      "chunks": [
        "main",
        "polyfills",
        "styles"
      ]
    }),
    //...some stuff...
    new AotPlugin({
      "mainPath": "main.ts",
      "entryModule": "app/app.module#AppModule",
      //...some stuff...
    })
  ],
  //...some more stuff...
};

¡Listo!

Si has seguido estos pasos, lo único que te queda por hacer es compilar el código y ver los resultados.

Ejecuta npm start

Toda la lógica de tu app Angular debería estarse ejecutando dentro de un WebWorker, consiguiendo una UI más fluida..

Nota adicional

npm start ejecuta el servidor de desarrollo webpack-dev, y por lo visto éste tiene algún problema con los WebWorkers y muestra un error por consola. Aún así, el WebWorker funciona correctamente (al menos en mi caso).

En todo caso, si compilas la app usando el comando webpack y utilizas cualquier otro servidor de desarrollo como por ejemplo simplehttpserver, no te encontrarás con ningún error 😉

¡Eso es todo amigos!

Si has disfrutado de este artículo, no olvides compartirlo. ¡Gracias!

Published inAngular 2Javascript

7 Comments

  1. Edy Avendaño Edy Avendaño

    Hi. It is possible to use it in ionic?

  2. Edy Avendaño Edy Avendaño

    Buenas. Muy buen post. Es posible utilizar los webworkers de esta manera en iónic?

    Saludos

    • Enrique Oriol Enrique Oriol

      De momento no, pero hace un tiempo su tech lead me comentó que estaban en ello.

      ¡Saludos!

      • Edy Avendaño Edy Avendaño

        Gracias por la respuesta. Espero que sea parte de la versión 4, seria genial.

        Saludos!

  3. Ricky Muñoz Ricky Muñoz

    Hola Enrique,
    Es posible ver el video de tu charla en linea? Saludos.

    • Enrique Oriol Enrique Oriol

      ¡Hola!

      Las charlas de comunidad no se grabaron, así que no 🙁

  4. Hola. Excelente post.

    Tengo una consulta que realizar, ¿alguno de ustedes a utilizado angular/material junto con webworkers?

    ¿Cual sería la mejor forma de usarlo?

    Yo creé un proyecto desde cero usando el angular cli, agregué las dependencias de @angular/material, y coloqué un botón en el html de app.component.

    Hasta ese momento todo funciona correctamente, es decir, el botón tiene las características gráficas correspondientes a un botón material.

    Luego realicé la conversión planteada en este post, para poder correr la aplicación en el webworker.

    Después de esto, la aplicación sigue funcionando, pero el botón deja de tener las características gráficas de un botón material.

    Gracias por el post, y si tienen alguna sugerencia para usar angular/material con webworkers, lo sabré apreciar.

Deja un comentario