Skip to content
This repository was archived by the owner on Dec 27, 2024. It is now read-only.

Commit 065fbe0

Browse files
committed
#10 garbage collecting
1 parent 80b2c4b commit 065fbe0

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

src/app/shared/services/storage.service.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,41 @@ describe('StorageService', () => {
314314
}
315315
});
316316

317+
it('garbage collects if told to', async (done) => {
318+
try {
319+
for (const type of stores) {
320+
await store[type].setItem('test', 'a value', 1, true).toPromise();
321+
expect(await store[type].getItem('test').toPromise()).toBe('a value', type);
322+
expect(await store[type].length().toPromise()).toBe(2, type);
323+
324+
await new Promise((resolve) => setTimeout(resolve, 1010));
325+
expect(await store[type].length().toPromise()).toBe(0, type);
326+
}
327+
328+
done();
329+
} catch (err) {
330+
done.fail(err);
331+
}
332+
});
333+
334+
it('does not garbage collect by default', async (done) => {
335+
try {
336+
for (const type of stores) {
337+
await store[type].setItem('test', 'a value', 1, false).toPromise();
338+
expect(await store[type].getItem('test').toPromise()).toBe('a value', type);
339+
expect(await store[type].length().toPromise()).toBe(2, type);
340+
341+
await new Promise((resolve) => setTimeout(resolve, 1010));
342+
expect(await store[type].length().toPromise()).toBe(2, type);
343+
expect(await store[type].getItem('test').toPromise()).toBe(null, type);
344+
}
345+
346+
done();
347+
} catch (err) {
348+
done.fail(err);
349+
}
350+
});
351+
317352
describe('getCachedObservable', () => {
318353
it('calls observable if not yet cached', async (done) => {
319354
try {

src/app/shared/services/storage.service.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import {
22
Injectable,
3-
InjectionToken,
43
NgModule
54
} from '@angular/core';
65
import * as localforage from 'localforage';
76
import {Observable} from 'rxjs/Observable';
87
import {Subject} from 'rxjs/Subject';
98

10-
export const WINDOW = new InjectionToken('Window');
119
export type StorageEngine = 'asyncStorage' | 'localStorageWrapper' | 'session' | 'webSQLStorage';
1210

1311
/**
@@ -40,6 +38,11 @@ export abstract class BaseStorageService {
4038
*/
4139
abstract get collectionName(): string;
4240

41+
/**
42+
* As garbage collected items are set, the reference to their timers are kept here. They are removed
43+
* upon completion, upon `setItem` with an existing timer being called, or upon `removeItem` being called
44+
*/
45+
private gcs = {};
4346
/**
4447
* As items are added, their Subjects are instantiated to allow Observing of that item via
4548
* [[StorageService.observe]].
@@ -201,6 +204,11 @@ export abstract class BaseStorageService {
201204
removeItem(key: string): Observable<void> {
202205
const p = async () => {
203206

207+
if (key in this.gcs) {
208+
clearTimeout(this.gcs[key]);
209+
delete this.gcs[key];
210+
}
211+
204212
await Promise.all([
205213
this.store.removeItem(this.getKey(key)),
206214
this.store.removeItem(this.getCacheKey(key))
@@ -235,6 +243,18 @@ export abstract class BaseStorageService {
235243
cacheTTL = +now;
236244
}
237245

246+
if (cacheTTL > 0 && gc) {
247+
if (key in this.gcs) {
248+
clearTimeout(this.gcs[key]);
249+
delete this.gcs[key];
250+
}
251+
252+
const ms = cacheTTL - +(new Date());
253+
if (ms > 0) {
254+
this.gcs[key] = setTimeout(() => this.removeItem(key), ms);
255+
}
256+
}
257+
238258
await Promise.all([
239259
this.store.setItem(this.getKey(key), value),
240260
this.store.setItem(this.getCacheKey(key), cacheTTL)

0 commit comments

Comments
 (0)