angularangular-cliweb-workershared-workerlovefield

How to configure Angular-CLI-generated project to include a SharedWorker


What are the specific steps to add a SharedWorker to an @angular/cli >1.2-generated project. I would like the SharedWorker to be defined in TypeScript (with full/correct type-definitions editor support), to share interfaces with the main project, and to be continuously compiled and tested along with the main project.

I haven't discovered an example or blog post that describes how to modify a cli-generated project to include either a Worker or a SharedWorker. However, there are several posts (such as this one) which show how to transform an entire cli-generated project to run it as a web worker. But that's not my use-case.

I want to confine interaction with a Lovefield database to a SharedWorker that runs in a separate process from the main application.

Though I've experimented a bit attempting to figure this out, I haven't made much progress. Hopefully someone can save me (and future readers) a lot of time.


Solution

  • I got this to work following these steps:

    1. Create folder src\app\shared-worker
    2. Create file src\app\shared-worker\shared-worker.d.ts with the following contents:

      SharedWorker definitions

      declare module SharedWorker {
          interface AbstractWorker extends EventTarget {
              onerror: (ev: ErrorEvent) => any;
          }
          export interface SharedWorker extends AbstractWorker {
              port: MessagePort;
              onconnect: (messageEvent: MessageEvent) => void;
          }
      }
      declare var SharedWorker: {
          prototype: SharedWorker.SharedWorker;
          new(stringUrl: string, name?: string): SharedWorker.SharedWorker;
      };
      // Merely added the onconnect() method to the file provied via:
      // npm install --save-dev retyped-sharedworker-tsd-ambient
      // Definitions by: Toshiya Nakakura <https://github.com/nakakura>
      
    3. Create file src\app\shared-worker\shared-worker.ts with the following demo contents:

      Sharedworker source

      /// <reference path=".\shared-worker.d.ts" />
      
      (<any>self).onconnect = (connectEvent: MessageEvent) => {
          const messagePort: MessagePort = (connectEvent.ports as MessagePort[])[0];
      
          messagePort.onmessage = function (messageEvent: MessageEvent) {
              const workerResult: number = messageEvent.data.firstNumber * messageEvent.data.secondNumber;
              messagePort.postMessage(workerResult);
          };
      
      };
      
    4. Update src\app\app.component.ts as follows:

      AppComponent

      import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
      import { Subject } from 'rxjs/Subject';
      import { Observable } from 'rxjs/Observable';
      
      @Component({
          selector: 'app-root',
          template: `
      <input type="number" [(ngModel)]="firstNumber" />
      <input type="number" [(ngModel)]="secondNumber" />
      <hr /><button (click)="postMessageToSharedWorker()">Invoke Shared Worker</button>
      <hr /><div>Result: {{result$ | async}}</div>`
      })
      export class AppComponent implements OnInit {
          public firstNumber = 4;
          public secondNumber = 8;
          private resultSubject = new Subject<number>();
          private sharedWorker: SharedWorker.SharedWorker;
          public result$: Observable<number>;
      
          public ngOnInit(): void {
              this.result$ = this.resultSubject.asObservable();
              if ('SharedWorker' in window) {
                  this.sharedWorker = new SharedWorker('app/shared-worker/shared-worker.js');
                  this.sharedWorker.port.onmessage = (messageEvent: MessageEvent) => {
                      this.resultSubject.next(messageEvent.data);
                      this.changeDetectorRef.detectChanges();
                  };
              }
          }
      
          public postMessageToSharedWorker() {
              if (!('SharedWorker' in window)) {
                  return;
              }
              this.sharedWorker.port.postMessage({ firstNumber: this.firstNumber, secondNumber: this.secondNumber });
          }
          constructor(private changeDetectorRef: ChangeDetectorRef) { }
      }
      
    5. Add "app/shared-worker/shared-worker.js" to the apps.assets node in .angular-cli.json

    6. Install concurrently as a dev dependency: npm i -D concurrently
    7. Create package.json scripts

      package.json

      "wrk-w": "tsc --noLib --experimentalDecorators --watch node_modules/typescript/lib/lib.es6.d.ts src/app/shared-worker/shared-worker.d.ts src/app/shared-worker/shared-worker.ts",
      "dev": "concurrently --kill-others \"npm run wrk-w\" \"npm run start\""