UNPKG

@ng-web-apis/workers

Version:
140 lines (132 loc) 4.21 kB
import { Observable, EMPTY, merge, fromEvent, tap, takeUntil, Subject, take, map } from 'rxjs'; import * as i0 from '@angular/core'; import { Pipe } from '@angular/core'; // throw an error using the `setTimeout` function // because web worker doesn't emit ErrorEvent from promises const WORKER_BLANK_FN = ` function(fn){ function isFunction(type){ return type === 'function'; } self.addEventListener('message', function(e) { var result = fn.call(null, e.data); if (result && [typeof result.then, typeof result.catch].every(isFunction)){ result .then(postMessage) .catch(function(error) { setTimeout(function(){throw error}, 0) }) } else { postMessage(result); } }) } `; class WebWorker extends Observable { worker; url; destroy$; constructor(url, options) { let worker; let error; try { worker = new Worker(url, options); } catch (e) { error = e; } super((subscriber) => { let eventStream$ = EMPTY; if (error) { subscriber.error(error); } else if (this.destroy$.isStopped) { subscriber.complete(); } else if (worker) { eventStream$ = merge(fromEvent(worker, 'message').pipe(tap((event) => subscriber.next(event))), fromEvent(worker, 'error').pipe(tap((event) => subscriber.error(event)))).pipe(takeUntil(this.destroy$)); } eventStream$.subscribe().add(subscriber); }); this.worker = worker; this.url = url; this.destroy$ = new Subject(); } static fromFunction(fn, options) { return new WebWorker(WebWorker.createFnUrl(fn), options); } static async execute(fn, data) { const worker = WebWorker.fromFunction(fn); // eslint-disable-next-line rxjs/no-topromise const promise = worker.pipe(take(1)).toPromise(); worker.postMessage(data); return promise.then((result) => { worker.terminate(); return result; }); } static createFnUrl(fn) { const script = `(${WORKER_BLANK_FN})(${fn});`; const blob = new Blob([script], { type: 'text/javascript' }); return URL.createObjectURL(blob); } terminate() { if (this.destroy$.isStopped) { return; } if (this.worker) { this.worker.terminate(); } URL.revokeObjectURL(this.url); this.destroy$.next(); this.destroy$.complete(); } postMessage(value) { if (this.worker) { this.worker.postMessage(value); } } } function toData() { return map(({ data }) => data); } class WorkerPipe { fn; worker; observer; transform(value, fn) { if (this.fn !== fn) { this.terminateWorker(); this.initNewWorker(fn); } this.worker.postMessage(value); return this.observer; } ngOnDestroy() { this.terminateWorker(); } terminateWorker() { if (this.worker) { this.worker.terminate(); } } initNewWorker(fn) { this.fn = fn; this.worker = WebWorker.fromFunction(fn); this.observer = this.worker.pipe(toData()); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WorkerPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: WorkerPipe, isStandalone: true, name: "waWorker" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: WorkerPipe, decorators: [{ type: Pipe, args: [{ standalone: true, name: 'waWorker', }] }] }); /** * Generated bundle index. Do not edit. */ export { WebWorker, WorkerPipe, toData }; //# sourceMappingURL=ng-web-apis-workers.mjs.map