Skip to content
This repository was archived by the owner on Oct 12, 2021. It is now read-only.

Commit 563e7ac

Browse files
committed
refactor(worker): remove dependency on rxjs
Refactor the service worker to completely remove RxJS in the worker bundle. Two main strategies are employed: 1) Promises are used for those actions which can be directly represented by them. 2) For cases where the lazy property of Observables was key, functions which return a Promise are used instead. 3) Promises resolve to a null value when an Observable would complete without emitting.
1 parent a333522 commit 563e7ac

File tree

16 files changed

+244
-303
lines changed

16 files changed

+244
-303
lines changed

service-worker/worker/src/companion/comm.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,28 @@ export class NgServiceWorker {
137137
// Sends a single message to the worker, and awaits one (or more) responses.
138138
private sendToWorker(worker: ServiceWorker, message: Object): Observable<any> {
139139
// A MessageChannel is sent with the message so responses can be correlated.
140-
let channel = new MessageChannel()
140+
const channel = new MessageChannel()
141141
// Observe replies.
142-
let result = Observable
143-
// Subscribe to port1's message event, which will deliver any response(s).
144-
.fromEvent(channel.port1, 'message')
145-
// Extract the data from the MessageEvent.
146-
.map((event: MessageEvent) => event.data)
142+
const result = new Observable<any>(observer => {
143+
let cancelId = null;
144+
const listener = (event: MessageEvent) => {
145+
const data = event.data;
146+
if (!!data && typeof data === "object" && data.hasOwnProperty('$ngsw') && data.hasOwnProperty('id')) {
147+
cancelId = data['id'];
148+
} else if (data === null) {
149+
observer.complete();
150+
channel.port1.removeEventListener('message', listener);
151+
return;
152+
} else {
153+
observer.next(data);
154+
}
155+
};
156+
channel.port1.addEventListener('message', listener);
157+
return () => {
158+
channel.port1.removeEventListener('message', listener);
159+
this.sendToWorker(worker, {cmd: 'cancel', id: cancelId});
160+
};
161+
})
147162
// Instead of complicating this with 'close' events, complete on a null value.
148163
.takeWhile(v => !!v)
149164
// The message will be sent before the consumer has a chance to subscribe to
Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import {
2-
LiteSubject,
32
Operation,
43
Plugin,
54
PluginFactory,
6-
VersionWorker
5+
VersionWorker,
76
} from '@angular/service-worker/worker';
87

9-
import {Observable} from 'rxjs/Observable';
10-
import {Observer} from 'rxjs/Observer';
11-
128
interface PushManifest {
139
showNotifications?: boolean;
1410
backgroundOnly?: boolean;
@@ -28,58 +24,53 @@ const NOTIFICATION_OPTION_NAMES = [
2824
'data'
2925
];
3026

31-
function pushesOp(push: PushImpl): Operation {
32-
const op: Operation = () => push.pushes;
33-
op.desc = {type: 'pushesOp', push};
34-
return op;
35-
}
36-
3727
export function Push(): PluginFactory<PushImpl> {
3828
return (worker: VersionWorker) => new PushImpl(worker);
3929
}
4030

4131
export class PushImpl implements Plugin<PushImpl> {
42-
43-
private pushBuffer: any[] = [];
44-
private pushSubject: LiteSubject<any> = new LiteSubject<any>();
45-
pushes: Observable<any>;
32+
private streams: number[] = [];
33+
private buffer: Object[] = [];
4634

4735
private get pushManifest(): PushManifest {
4836
return this.worker.manifest['push'] as PushManifest || EMPTY_MANIFEST;
4937
}
5038

51-
constructor(private worker: VersionWorker) {
52-
this.pushes = Observable.create((observer: Observer<any>) => {
53-
if (this.pushBuffer !== null) {
54-
this.pushBuffer.forEach(data => observer.next(data));
55-
}
56-
this.pushBuffer = null;
57-
const sub = this.pushSubject.observable.subscribe(observer);
58-
return () => {
59-
sub.unsubscribe();
60-
if (!this.pushSubject.hasSubscribers) {
61-
this.pushBuffer = [];
62-
}
63-
};
64-
});
65-
}
39+
constructor(private worker: VersionWorker) {}
6640

6741
setup(ops: Operation[]): void {}
6842

69-
message(message: any, ops: Operation[]): void {
43+
message(message: any, id: number): void {
7044
switch (message['cmd']) {
7145
case 'push':
72-
ops.push(pushesOp(this));
46+
this.streams.push(id);
47+
if (this.buffer !== null) {
48+
this.buffer.forEach(message => this.worker.sendToStream(id, message));
49+
this.buffer = null;
50+
}
7351
break;
7452
}
7553
}
7654

55+
messageClosed(id: number): void {
56+
const index = this.streams.indexOf(id);
57+
if (index === -1) {
58+
return;
59+
}
60+
this.streams.splice(index, 1);
61+
if (this.streams.length === 0) {
62+
this.buffer = [];
63+
}
64+
}
65+
7766
push(data: any): void {
7867
this.maybeShowNotification(data);
79-
if (this.pushBuffer === null) {
80-
this.pushSubject.next(data);
68+
if (this.buffer !== null) {
69+
this.buffer.push(data);
8170
} else {
82-
this.pushBuffer.push(data);
71+
this.streams.forEach(id => {
72+
this.worker.sendToStream(id, data);
73+
})
8374
}
8475
}
8576

@@ -88,7 +79,7 @@ export class PushImpl implements Plugin<PushImpl> {
8879
return;
8980
}
9081
const manifest = this.pushManifest;
91-
if (!manifest.showNotifications || (!!manifest.backgroundOnly && this.pushBuffer === null)) {
82+
if (!manifest.showNotifications || (!!manifest.backgroundOnly && this.buffer === null)) {
9283
return;
9384
}
9485
const desc = data.notification as Object;
@@ -98,4 +89,4 @@ export class PushImpl implements Plugin<PushImpl> {
9889
.forEach(name => options[name] = desc[name]);
9990
this.worker.showNotification(desc['title'], options);
10091
}
101-
}
92+
}

service-worker/worker/src/plugins/static/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
LOG,
1212
Verbosity
1313
} from '@angular/service-worker/worker';
14-
import {Observable} from 'rxjs/Observable';
1514

1615
interface UrlToHashMap {
1716
[url: string]: string;
@@ -31,7 +30,6 @@ export function StaticContentCache(options?: StaticContentCacheOptions): PluginF
3130
}
3231

3332
export class StaticContentCacheImpl implements Plugin<StaticContentCacheImpl> {
34-
3533
private cacheKey: string;
3634

3735
constructor(public worker: VersionWorker, public key: string) {
@@ -50,10 +48,10 @@ export class StaticContentCacheImpl implements Plugin<StaticContentCacheImpl> {
5048
.worker
5149
.cache
5250
.load(this.cacheKey, url)
53-
.switchMap(resp => {
51+
.then(resp => {
5452
if (!!resp) {
5553
LOG.technical(`setup(${this.cacheKey}, ${url}): no need to refresh ${url} in the cache`);
56-
return Observable.empty();
54+
return null;
5755
}
5856
LOG.technical(`setup(${this.cacheKey}, ${url}): caching from network`);
5957
return cacheFromNetworkOp(this.worker, url, this.cacheKey)();

service-worker/worker/src/test/unit/worker.spec.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const HASHED_MANIFEST_2 = JSON.stringify({
5858
});
5959

6060
const consoleLogger = new ConsoleHandler();
61-
LOGGER.messages.subscribe(entry => consoleLogger.handle(entry));
61+
LOGGER.messages = (entry => consoleLogger.handle(entry));
6262
LOGGER.release();
6363

6464
function errored(err, done) {
@@ -107,8 +107,6 @@ function createServiceWorker(scope, adapter, cache, fetch, events) {
107107
return new Driver(MANIFEST_URL, plugins, scope, adapter, cache, events, fetch);
108108
}
109109

110-
LOG
111-
112110
describe('ngsw', () => {
113111
const simpleManifestCache = `manifest:${SIMPLE_MANIFEST_HASH}:static`;
114112
describe('initial load', () => {

service-worker/worker/src/worker/api.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,40 @@
1-
import {Observable} from 'rxjs/Observable';
2-
31
import {NgSwAdapter, NgSwCache} from './facade';
42
import {Manifest} from './manifest';
53

6-
export interface CustomOperator<T> {
7-
(obs: Observable<T>): Observable<T>;
8-
}
9-
104
export interface FetchInstruction {
11-
(): Observable<Response>;
5+
(): Promise<Response>;
126
desc?: Object;
137
}
148

159
export interface Operation {
16-
(): Observable<any>;
10+
(): Promise<any>;
1711
desc?: Object;
1812
}
1913

20-
export interface VersionWorker {
14+
export interface VersionWorker extends StreamController {
2115
readonly manifest: Manifest;
2216
readonly cache: NgSwCache;
2317
readonly adapter: NgSwAdapter;
2418

25-
refresh(req: Request): Observable<Response>;
26-
fetch(req: Request): Observable<Response>;
19+
refresh(req: Request): Promise<Response>;
20+
fetch(req: Request): Promise<Response>;
2721
showNotification(title: string, options?: Object): void;
22+
sendToStream(id: number, message: Object): void;
23+
closeStream(id: number): void;
24+
}
25+
26+
export interface StreamController {
27+
sendToStream(id: number, message: Object): void;
28+
closeStream(id: number): void;
2829
}
2930

3031
export interface Plugin<T extends Plugin<T>> {
3132
setup(operations: Operation[]): void;
3233
update?(operations: Operation[], previous: T): void;
3334
fetch?(req: Request, instructions: FetchInstruction[]): void;
3435
cleanup?(operations: Operation[]): void;
35-
message?(message: any, operations: Operation[]): void;
36+
message?(message: any, id: number): void;
37+
messageClosed?(id: number);
3638
push?(data: any): void;
3739
}
3840

service-worker/worker/src/worker/bootstrap.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class NgSwBrowserAdapter implements NgSwAdapter {
3131
return new Request(req, init);
3232
}
3333

34-
newResponse(body: string | Blob, init?: Object): Response {
34+
newResponse(body: string | Blob, init?: ResponseInit): Response {
3535
return new Response(body, init);
3636
}
3737
}
@@ -53,7 +53,7 @@ export function bootstrapServiceWorker(options?: BootstrapOptions): Driver {
5353
const fetch = new NgSwFetch(scope, adapter);
5454
LOGGER.setVerbosity(options.logLevel);
5555
if (!!options.logHandlers) {
56-
LOGGER.messages.subscribe(entry => options.logHandlers.forEach(handler => handler.handle(entry)));
56+
LOGGER.messages = (entry => options.logHandlers.forEach(handler => handler.handle(entry)));
5757
}
5858
LOGGER.release();
5959
return new Driver(manifestUrl, plugins, scope, adapter, cache, events, fetch);
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import {NgSwCache} from './facade';
22

3-
import {Observable} from 'rxjs/Observable';
4-
53
export class ScopedCache implements NgSwCache {
64

75
constructor(private delegate: NgSwCache, private prefix: string) {}
@@ -10,15 +8,15 @@ export class ScopedCache implements NgSwCache {
108
return this.delegate.load(this.prefix + cache, req);
119
}
1210

13-
store(cache: string, req: string | Request, resp: Response): Observable<any> {
11+
store(cache: string, req: string | Request, resp: Response): Promise<any> {
1412
return this.delegate.store(this.prefix + cache, req, resp);
1513
}
1614

17-
remove(cache: string): Observable<any> {
15+
remove(cache: string): Promise<any> {
1816
return this.delegate.remove(this.prefix + cache);
1917
}
2018

21-
invalidate(cache: string, req: string | Request): Observable<void> {
19+
invalidate(cache: string, req: string | Request): Promise<void> {
2220
return this.delegate.invalidate(this.prefix + cache, req);
2321
}
2422
}

service-worker/worker/src/worker/common.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ import {FetchInstruction, Operation, VersionWorker} from './api';
22
import {LOG} from './logging';
33
import {VersionWorkerImpl} from './worker';
44

5-
import {Observable} from 'rxjs/Observable';
6-
75
export function cacheFromNetworkOp(worker: VersionWorker, url: string, cache: string): Operation {
86
const op: Operation = () => worker
97
.refresh(worker.adapter.newRequest(url))
10-
.switchMap(resp => worker.cache.store(cache, url, resp));
8+
.then(resp => worker.cache.store(cache, url, resp));
119
op.desc = {type: 'cacheFromNetworkOp', worker, url, cache};
1210
return op;
1311
}
@@ -16,20 +14,21 @@ export function copyExistingCacheOp(oldWorker: VersionWorker, newWorker: Version
1614
const op: Operation = () => oldWorker
1715
.cache
1816
.load(cache, url)
19-
.switchMap(resp => !!resp
20-
? newWorker.cache.store(cache, url, resp)
21-
: Observable.empty());
17+
.then(resp => !!resp
18+
? newWorker.cache.store(cache, url, resp).then(() => true)
19+
: null);
2220
op.desc = {type: 'copyExistingCacheOp', oldWorker, newWorker, url, cache};
2321
return op;
2422
}
2523

2624
export function copyExistingOrFetchOp(oldWorker: VersionWorker, newWorker: VersionWorker, url: string, cache: string): Operation {
27-
const op: Operation = () => Observable
28-
.concat(
29-
copyExistingCacheOp(oldWorker, newWorker, url, cache)(),
30-
cacheFromNetworkOp(newWorker, url, cache)()
31-
)
32-
.take(1);
25+
const op: Operation = () => copyExistingCacheOp(oldWorker, newWorker, url, cache)()
26+
.then(res => {
27+
if (!res) {
28+
return cacheFromNetworkOp(newWorker, url, cache)();
29+
}
30+
return res;
31+
});
3332
op.desc = {type: 'copyExistingOrFetchOp', oldWorker, newWorker, url, cache};
3433
return op;
3534
}
@@ -41,24 +40,20 @@ export function deleteCacheOp(worker: VersionWorker, key: string): Operation {
4140
}
4241

4342
export function fetchFromCacheInstruction(worker: VersionWorker, req: string | Request, cache: string): FetchInstruction {
44-
const op: FetchInstruction = () => worker
45-
.cache
46-
.load(cache, req)
47-
.filter(v => !!v);
43+
const op: FetchInstruction = () => worker.cache.load(cache, req);
4844
op.desc = {type: 'fetchFromCacheInstruction', worker, req, cache};
4945
return op;
5046
}
5147

5248
export function fetchFromNetworkInstruction(worker: VersionWorker, req: Request, shouldRefresh: boolean = true): FetchInstruction {
53-
const op: FetchInstruction = () => shouldRefresh ? worker.refresh(req) : (worker as VersionWorkerImpl).scope.fetch(req);
49+
const op: FetchInstruction = () => shouldRefresh ? worker.refresh(req) : (worker as any as VersionWorkerImpl).scope.fetch(req);
5450
op.desc = {type: 'fetchFromNetworkInstruction', worker, req};
5551
return op;
5652
}
5753

5854
export function rewriteUrlInstruction(worker: VersionWorker, req: Request, destUrl: string): FetchInstruction {
5955
const newReq = worker.adapter.newRequest(destUrl);
60-
const op: FetchInstruction = () => worker
61-
.fetch(newReq);
56+
const op: FetchInstruction = () => worker.fetch(newReq);
6257
op.desc = {type: 'rewriteUrlInstruction', worker, req, destUrl};
6358
return op;
6459
}

0 commit comments

Comments
 (0)