import { Injectable } from '@angular/core' ; type CallbackFunction = ( ) => void ; @ Injectable ( ) export class WebworkerService { private workerFunctionToUrlMap = new WeakMap < CallbackFunction , string > ( ) ; private promiseToWorkerMap = new WeakMap < Promise < any > , Worker > ( ) ; run < T > ( workerFunction , data ? ) { const url = this . getOrCreateWorkerUrl ( workerFunction ) ; return this . runUrl ( url , data ) ; } runUrl ( url , data ? ) { const worker = new Worker ( url ) ; const promise = this . createPromiseForWorker ( worker , data ) ; const promiseCleaner = this . createPromiseCleaner ( promise ) ; this . promiseToWorkerMap . set ( promise , worker ) ; promise . then ( promiseCleaner ) . catch ( promiseCleaner ) ; return promise ; } terminate < T > ( promise ) { return this . removePromise ( promise ) ; } getWorker ( promise ) { return this . promiseToWorkerMap . get ( promise ) ; } private createPromiseForWorker < T > ( worker , data ) { return new Promise < T > ( ( resolve , reject ) => { worker . addEventListener ( 'message' , ( event ) => resolve ( event . data ) ) ; worker . addEventListener ( 'error' , reject ) ; worker . postMessage ( data ) ; } ) ; } private getOrCreateWorkerUrl ( fn ) { if ( ! this . workerFunctionToUrlMap . has ( fn ) ) { const url = this . createWorkerUrl ( fn ) ; this . workerFunctionToUrlMap . set ( fn , url ) ; return url ; } return this . workerFunctionToUrlMap . get ( fn ) ; } private createWorkerUrl ( resolve ) { const resolveString = resolve . toString ( ) ; const webWorkerTemplate = `
      self.addEventListener('message', function(e) {
        postMessage((${ resolveString })(e.data));
      });
    ` ; const blob = new Blob ( [ webWorkerTemplate ] , { type : 'text/javascript' } ) ; return URL . createObjectURL ( blob ) ; } private createPromiseCleaner < T > ( promise ) { return ( event ) => { this . removePromise ( promise ) ; return event ; } ; } private removePromise < T > ( promise ) { const worker = this . promiseToWorkerMap . get ( promise ) ; if ( worker ) { worker . terminate ( ) ; } this . promiseToWorkerMap . delete ( promise ) ; return promise ; } }