Skip to content
This repository was archived by the owner on May 29, 2023. It is now read-only.

Commit 3dfd75e

Browse files
committed
feat: added service and base classes
1 parent 149bb8e commit 3dfd75e

File tree

10 files changed

+147
-1
lines changed

10 files changed

+147
-1
lines changed

projects/demo/src/app/app.browser.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import {NgModule} from '@angular/core';
88
import {FormsModule} from '@angular/forms';
99
import {BrowserModule} from '@angular/platform-browser';
10+
import {WebWorkerModule} from '@ng-web-apis/workers';
1011
import {AppComponent} from './app.component';
1112
import {AppRoutingModule} from './app.routes';
1213

@@ -17,6 +18,7 @@ import {AppRoutingModule} from './app.routes';
1718
FormsModule,
1819
BrowserModule.withServerTransition({appId: 'demo'}),
1920
AppRoutingModule,
21+
WebWorkerModule,
2022
],
2123
declarations: [AppComponent],
2224
providers: [
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<form (ngSubmit)="workerThread.next(input.value)">
2+
<input #input />
3+
<button type="submit">Send</button>
4+
<button type="button" (click)="workerThread.complete()">Complete</button>
5+
</form>
6+
7+
{{ workerThread | async | json }}
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
import {ChangeDetectionStrategy, Component} from '@angular/core';
2+
import {WebWorker, WebWorkerExecutor} from '@ng-web-apis/workers';
23

34
@Component({
45
selector: 'main',
56
templateUrl: './app.component.html',
67
styleUrls: ['./app.component.less'],
78
changeDetection: ChangeDetectionStrategy.OnPush,
89
})
9-
export class AppComponent {}
10+
export class AppComponent {
11+
public workerThread: WebWorker<string, string>;
12+
13+
constructor(webWorkerExecutor: WebWorkerExecutor) {
14+
this.workerThread = webWorkerExecutor.execute((result: string) =>
15+
Promise.resolve(`Message from worker: ${result}`),
16+
);
17+
}
18+
}

projects/workers/src/public-api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
/**
22
* Public API Surface of @ng-web-apis/workers
33
*/
4+
export * from './web-worker/web-worker.module';
5+
export * from './web-worker/types/web-worker-function';
6+
export * from './web-worker/services/web-worker-executor.service';
7+
export * from './web-worker/classes/web-worker';
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {Subject} from 'rxjs';
2+
3+
export class AnyNextSubject<T> extends Subject<T> {
4+
next(value?: any) {
5+
super.next(value);
6+
}
7+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {fromEvent} from 'rxjs';
2+
import {takeWhile} from 'rxjs/operators';
3+
import {WebWorkerFunction} from '../types/web-worker-function';
4+
import {AnyNextSubject} from './any-next-subject';
5+
6+
export class WebWorker<T, R> extends AnyNextSubject<R> {
7+
private worker!: Worker;
8+
9+
private static createFnUrl(fn: WebWorkerFunction): string {
10+
const script = `
11+
self.addEventListener('message', function(e) {
12+
var result = ((${fn.toString()}).call(null, e.data));
13+
if(result && [typeof result.then, typeof result.catch].every(function (type) {return type === 'function'})){
14+
result.then(function(res){
15+
postMessage({result: res});
16+
}).catch(function(error){
17+
postMessage({error: error});
18+
})
19+
} else {
20+
postMessage(result);
21+
}
22+
});
23+
`;
24+
25+
const blob = new Blob([script], {type: 'text/javascript'});
26+
27+
return URL.createObjectURL(blob);
28+
}
29+
30+
public static fromFunction<T, R>(
31+
fn: WebWorkerFunction<T, R>,
32+
options?: WorkerOptions,
33+
): WebWorker<T, R> {
34+
return new WebWorker<T, R>(WebWorker.createFnUrl(fn), options);
35+
}
36+
37+
constructor(private url: string, options?: WorkerOptions) {
38+
super();
39+
40+
try {
41+
this.worker = new Worker(url, options);
42+
} catch (e) {
43+
this.error(e);
44+
}
45+
46+
fromEvent<MessageEvent>(this.worker, 'message')
47+
.pipe(takeWhile(() => !this.isStopped))
48+
.subscribe(event => {
49+
if (event.data) {
50+
if (event.data.hasOwnProperty('error')) {
51+
this.error(event.data.error);
52+
} else if (event.data.hasOwnProperty('result')) {
53+
super.next(event.data.result);
54+
} else {
55+
super.next();
56+
}
57+
}
58+
});
59+
60+
fromEvent<ErrorEvent>(this.worker, 'error')
61+
.pipe(takeWhile(() => !this.isStopped))
62+
.subscribe(event => {
63+
this.error(event.error);
64+
});
65+
}
66+
67+
complete() {
68+
this.worker.terminate();
69+
URL.revokeObjectURL(this.url);
70+
super.complete();
71+
}
72+
73+
next(value?: T) {
74+
if (!this.isStopped) {
75+
this.worker.postMessage(value);
76+
}
77+
}
78+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import {TestBed} from '@angular/core/testing';
2+
3+
import {WebWorkerExecutor} from './web-worker-executor.service';
4+
5+
describe('WebWorkerExecutorService', () => {
6+
beforeEach(() => TestBed.configureTestingModule({}));
7+
8+
it('should be created', () => {
9+
const service: WebWorkerExecutor = TestBed.get(WebWorkerExecutor);
10+
11+
expect(service).toBeTruthy();
12+
});
13+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import {inject, Injectable, InjectFlags} from '@angular/core';
2+
import {WebWorker} from '../classes/web-worker';
3+
import {WebWorkerFunction} from '../types/web-worker-function';
4+
import {WebWorkerModule} from '../web-worker.module';
5+
6+
@Injectable({
7+
providedIn: WebWorkerModule,
8+
useFactory(): WebWorkerExecutor {
9+
const instance = inject(WebWorkerExecutor, InjectFlags.Optional);
10+
11+
return instance || new WebWorkerExecutor();
12+
},
13+
})
14+
export class WebWorkerExecutor {
15+
execute<T, R>(fn: WebWorkerFunction<T, R>, options?: WorkerOptions): WebWorker<T, R> {
16+
return WebWorker.fromFunction<T, R>(fn, options);
17+
}
18+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type WebWorkerFunction<T = any, R = any> = (data: T) => R | PromiseLike<R>;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {CommonModule} from '@angular/common';
2+
import {NgModule} from '@angular/core';
3+
4+
@NgModule({
5+
imports: [CommonModule],
6+
})
7+
export class WebWorkerModule {}

0 commit comments

Comments
 (0)