@ng-web-apis/workers
Version:
A library for comfortable use of Web Workers API in Angular
140 lines (132 loc) • 4.21 kB
JavaScript
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