refactor(service-worker): minor refactorings to improve readability/maintainability (#42622)
This commit makes some minor refactorings to improve the code readability and maintainability, including: - Avoiding code duplication. - Using more descriptive variable names. - Using `async/await` instead of `Promise#then()`. - Accessing variables directly instead of via `this` when possible. PR Close #42622
This commit is contained in:
parent
874de59d35
commit
4962ef5330
|
@ -68,37 +68,33 @@ export class AppVersion implements UpdateSource {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private scope: ServiceWorkerGlobalScope, private adapter: Adapter, private database: Database,
|
private scope: ServiceWorkerGlobalScope, private adapter: Adapter, private database: Database,
|
||||||
private idle: IdleScheduler, private debugHandler: DebugHandler, readonly manifest: Manifest,
|
idle: IdleScheduler, private debugHandler: DebugHandler, readonly manifest: Manifest,
|
||||||
readonly manifestHash: string) {
|
readonly manifestHash: string) {
|
||||||
// The hashTable within the manifest is an Object - convert it to a Map for easier lookups.
|
// The hashTable within the manifest is an Object - convert it to a Map for easier lookups.
|
||||||
Object.keys(this.manifest.hashTable).forEach(url => {
|
Object.keys(manifest.hashTable).forEach(url => {
|
||||||
this.hashTable.set(adapter.normalizeUrl(url), this.manifest.hashTable[url]);
|
this.hashTable.set(adapter.normalizeUrl(url), manifest.hashTable[url]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process each `AssetGroup` declared in the manifest. Each declared group gets an `AssetGroup`
|
// Process each `AssetGroup` declared in the manifest. Each declared group gets an `AssetGroup`
|
||||||
// instance
|
// instance created for it, of a type that depends on the configuration mode.
|
||||||
// created for it, of a type that depends on the configuration mode.
|
const assetCacheNamePrefix = `${adapter.cacheNamePrefix}:${manifestHash}:assets`;
|
||||||
this.assetGroups = (manifest.assetGroups || []).map(config => {
|
this.assetGroups = (manifest.assetGroups || []).map(config => {
|
||||||
// Every asset group has a cache that's prefixed by the manifest hash and the name of the
|
|
||||||
// group.
|
|
||||||
const prefix = `${adapter.cacheNamePrefix}:${this.manifestHash}:assets`;
|
|
||||||
// Check the caching mode, which determines when resources will be fetched/updated.
|
// Check the caching mode, which determines when resources will be fetched/updated.
|
||||||
switch (config.installMode) {
|
switch (config.installMode) {
|
||||||
case 'prefetch':
|
case 'prefetch':
|
||||||
return new PrefetchAssetGroup(
|
return new PrefetchAssetGroup(
|
||||||
this.scope, this.adapter, this.idle, config, this.hashTable, this.database, prefix);
|
scope, adapter, idle, config, this.hashTable, database, assetCacheNamePrefix);
|
||||||
case 'lazy':
|
case 'lazy':
|
||||||
return new LazyAssetGroup(
|
return new LazyAssetGroup(
|
||||||
this.scope, this.adapter, this.idle, config, this.hashTable, this.database, prefix);
|
scope, adapter, idle, config, this.hashTable, database, assetCacheNamePrefix);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Process each `DataGroup` declared in the manifest.
|
// Process each `DataGroup` declared in the manifest.
|
||||||
this.dataGroups =
|
this.dataGroups = (manifest.dataGroups || [])
|
||||||
(manifest.dataGroups || [])
|
|
||||||
.map(
|
.map(
|
||||||
config => new DataGroup(
|
config => new DataGroup(
|
||||||
this.scope, this.adapter, config, this.database, this.debugHandler,
|
scope, adapter, config, database, debugHandler,
|
||||||
`${adapter.cacheNamePrefix}:${config.version}:data`));
|
`${adapter.cacheNamePrefix}:${config.version}:data`));
|
||||||
|
|
||||||
// This keeps backwards compatibility with app versions without navigation urls.
|
// This keeps backwards compatibility with app versions without navigation urls.
|
||||||
|
|
|
@ -55,7 +55,8 @@ export abstract class AssetGroup {
|
||||||
constructor(
|
constructor(
|
||||||
protected scope: ServiceWorkerGlobalScope, protected adapter: Adapter,
|
protected scope: ServiceWorkerGlobalScope, protected adapter: Adapter,
|
||||||
protected idle: IdleScheduler, protected config: AssetGroupConfig,
|
protected idle: IdleScheduler, protected config: AssetGroupConfig,
|
||||||
protected hashes: Map<string, string>, protected db: Database, protected prefix: string) {
|
protected hashes: Map<string, string>, protected db: Database,
|
||||||
|
protected cacheNamePrefix: string) {
|
||||||
this.name = config.name;
|
this.name = config.name;
|
||||||
|
|
||||||
// Normalize the config's URLs to take the ServiceWorker's scope into account.
|
// Normalize the config's URLs to take the ServiceWorker's scope into account.
|
||||||
|
@ -65,13 +66,13 @@ export abstract class AssetGroup {
|
||||||
this.patterns = config.patterns.map(pattern => new RegExp(pattern));
|
this.patterns = config.patterns.map(pattern => new RegExp(pattern));
|
||||||
|
|
||||||
// This is the primary cache, which holds all of the cached requests for this group. If a
|
// This is the primary cache, which holds all of the cached requests for this group. If a
|
||||||
// resource
|
// resource isn't in this cache, it hasn't been fetched yet.
|
||||||
// isn't in this cache, it hasn't been fetched yet.
|
this.cache = scope.caches.open(`${cacheNamePrefix}:${config.name}:cache`);
|
||||||
this.cache = scope.caches.open(`${this.prefix}:${config.name}:cache`);
|
|
||||||
|
|
||||||
// This is the metadata table, which holds specific information for each cached URL, such as
|
// This is the metadata table, which holds specific information for each cached URL, such as
|
||||||
// the timestamp of when it was added to the cache.
|
// the timestamp of when it was added to the cache.
|
||||||
this.metadata = this.db.open(`${this.prefix}:${config.name}:meta`, config.cacheQueryOptions);
|
this.metadata =
|
||||||
|
this.db.open(`${cacheNamePrefix}:${config.name}:meta`, config.cacheQueryOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async cacheStatus(url: string): Promise<UpdateCacheStatus> {
|
async cacheStatus(url: string): Promise<UpdateCacheStatus> {
|
||||||
|
@ -102,8 +103,8 @@ export abstract class AssetGroup {
|
||||||
* Clean up all the cached data for this group.
|
* Clean up all the cached data for this group.
|
||||||
*/
|
*/
|
||||||
async cleanup(): Promise<void> {
|
async cleanup(): Promise<void> {
|
||||||
await this.scope.caches.delete(`${this.prefix}:${this.config.name}:cache`);
|
await this.scope.caches.delete(`${this.cacheNamePrefix}:${this.config.name}:cache`);
|
||||||
await this.db.delete(`${this.prefix}:${this.config.name}:meta`);
|
await this.db.delete(`${this.cacheNamePrefix}:${this.config.name}:meta`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -136,7 +137,8 @@ export abstract class AssetGroup {
|
||||||
// to make sure it's still usable.
|
// to make sure it's still usable.
|
||||||
if (await this.needToRevalidate(req, cachedResponse)) {
|
if (await this.needToRevalidate(req, cachedResponse)) {
|
||||||
this.idle.schedule(
|
this.idle.schedule(
|
||||||
`revalidate(${this.prefix}, ${this.config.name}): ${req.url}`, async () => {
|
`revalidate(${this.cacheNamePrefix}, ${this.config.name}): ${req.url}`,
|
||||||
|
async () => {
|
||||||
await this.fetchAndCacheOnce(req);
|
await this.fetchAndCacheOnce(req);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -314,7 +316,7 @@ export abstract class AssetGroup {
|
||||||
try {
|
try {
|
||||||
// This response is safe to cache (as long as it's cloned). Wait until the cache operation
|
// This response is safe to cache (as long as it's cloned). Wait until the cache operation
|
||||||
// is complete.
|
// is complete.
|
||||||
const cache = await this.scope.caches.open(`${this.prefix}:${this.config.name}:cache`);
|
const cache = await this.cache;
|
||||||
await cache.put(req, res.clone());
|
await cache.put(req, res.clone());
|
||||||
|
|
||||||
// If the request is not hashed, update its metadata, especially the timestamp. This is
|
// If the request is not hashed, update its metadata, especially the timestamp. This is
|
||||||
|
|
|
@ -247,13 +247,13 @@ export class DataGroup {
|
||||||
constructor(
|
constructor(
|
||||||
private scope: ServiceWorkerGlobalScope, private adapter: Adapter,
|
private scope: ServiceWorkerGlobalScope, private adapter: Adapter,
|
||||||
private config: DataGroupConfig, private db: Database, private debugHandler: DebugHandler,
|
private config: DataGroupConfig, private db: Database, private debugHandler: DebugHandler,
|
||||||
private prefix: string) {
|
private cacheNamePrefix: string) {
|
||||||
this.patterns = this.config.patterns.map(pattern => new RegExp(pattern));
|
this.patterns = config.patterns.map(pattern => new RegExp(pattern));
|
||||||
this.cache = this.scope.caches.open(`${this.prefix}:dynamic:${this.config.name}:cache`);
|
this.cache = scope.caches.open(`${cacheNamePrefix}:dynamic:${config.name}:cache`);
|
||||||
this.lruTable = this.db.open(
|
this.lruTable =
|
||||||
`${this.prefix}:dynamic:${this.config.name}:lru`, this.config.cacheQueryOptions);
|
this.db.open(`${cacheNamePrefix}:dynamic:${config.name}:lru`, config.cacheQueryOptions);
|
||||||
this.ageTable = this.db.open(
|
this.ageTable =
|
||||||
`${this.prefix}:dynamic:${this.config.name}:age`, this.config.cacheQueryOptions);
|
this.db.open(`${cacheNamePrefix}:dynamic:${config.name}:age`, config.cacheQueryOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -550,9 +550,9 @@ export class DataGroup {
|
||||||
async cleanup(): Promise<void> {
|
async cleanup(): Promise<void> {
|
||||||
// Remove both the cache and the database entries which track LRU stats.
|
// Remove both the cache and the database entries which track LRU stats.
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.scope.caches.delete(`${this.prefix}:dynamic:${this.config.name}:cache`),
|
this.scope.caches.delete(`${this.cacheNamePrefix}:dynamic:${this.config.name}:cache`),
|
||||||
this.db.delete(`${this.prefix}:dynamic:${this.config.name}:age`),
|
this.db.delete(`${this.cacheNamePrefix}:dynamic:${this.config.name}:age`),
|
||||||
this.db.delete(`${this.prefix}:dynamic:${this.config.name}:lru`),
|
this.db.delete(`${this.cacheNamePrefix}:dynamic:${this.config.name}:lru`),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ import {Database, NotFound, Table} from './database';
|
||||||
* state within mock `Response` objects.
|
* state within mock `Response` objects.
|
||||||
*/
|
*/
|
||||||
export class CacheDatabase implements Database {
|
export class CacheDatabase implements Database {
|
||||||
private tables = new Map<string, Promise<CacheTable>>();
|
private cacheNamePrefix = `${this.adapter.cacheNamePrefix}:db`;
|
||||||
|
private tables = new Map<string, CacheTable>();
|
||||||
|
|
||||||
constructor(private scope: ServiceWorkerGlobalScope, private adapter: Adapter) {}
|
constructor(private scope: ServiceWorkerGlobalScope, private adapter: Adapter) {}
|
||||||
|
|
||||||
|
@ -23,19 +24,20 @@ export class CacheDatabase implements Database {
|
||||||
if (this.tables.has(name)) {
|
if (this.tables.has(name)) {
|
||||||
this.tables.delete(name);
|
this.tables.delete(name);
|
||||||
}
|
}
|
||||||
return this.scope.caches.delete(`${this.adapter.cacheNamePrefix}:db:${name}`);
|
return this.scope.caches.delete(`${this.cacheNamePrefix}:${name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
list(): Promise<string[]> {
|
async list(): Promise<string[]> {
|
||||||
return this.scope.caches.keys().then(
|
const prefix = `${this.cacheNamePrefix}:`;
|
||||||
keys => keys.filter(key => key.startsWith(`${this.adapter.cacheNamePrefix}:db:`)));
|
const allCacheNames = await this.scope.caches.keys();
|
||||||
|
const dbCacheNames = allCacheNames.filter(name => name.startsWith(prefix));
|
||||||
|
return dbCacheNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
open(name: string, cacheQueryOptions?: CacheQueryOptions): Promise<Table> {
|
async open(name: string, cacheQueryOptions?: CacheQueryOptions): Promise<Table> {
|
||||||
if (!this.tables.has(name)) {
|
if (!this.tables.has(name)) {
|
||||||
const table =
|
const cache = await this.scope.caches.open(`${this.cacheNamePrefix}:${name}`);
|
||||||
this.scope.caches.open(`${this.adapter.cacheNamePrefix}:db:${name}`)
|
const table = new CacheTable(name, cache, this.adapter, cacheQueryOptions);
|
||||||
.then(cache => new CacheTable(name, cache, this.adapter, cacheQueryOptions));
|
|
||||||
this.tables.set(name, table);
|
this.tables.set(name, table);
|
||||||
}
|
}
|
||||||
return this.tables.get(name)!;
|
return this.tables.get(name)!;
|
||||||
|
|
|
@ -556,8 +556,7 @@ export class Driver implements Debuggable, UpdateSource {
|
||||||
// server and building up an empty initial state.
|
// server and building up an empty initial state.
|
||||||
const manifest = await this.fetchLatestManifest();
|
const manifest = await this.fetchLatestManifest();
|
||||||
const hash = hashManifest(manifest);
|
const hash = hashManifest(manifest);
|
||||||
manifests = {};
|
manifests = {[hash]: manifest};
|
||||||
manifests[hash] = manifest;
|
|
||||||
assignments = {};
|
assignments = {};
|
||||||
latest = {latest: hash};
|
latest = {latest: hash};
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ export class MockClients implements Clients {
|
||||||
export class SwTestHarness extends Adapter implements ServiceWorkerGlobalScope, Context {
|
export class SwTestHarness extends Adapter implements ServiceWorkerGlobalScope, Context {
|
||||||
readonly clients = new MockClients();
|
readonly clients = new MockClients();
|
||||||
private eventHandlers = new Map<string, Function>();
|
private eventHandlers = new Map<string, Function>();
|
||||||
private skippedWaiting = true;
|
private skippedWaiting = false;
|
||||||
|
|
||||||
private selfMessageQueue: any[] = [];
|
private selfMessageQueue: any[] = [];
|
||||||
autoAdvanceTime = false;
|
autoAdvanceTime = false;
|
||||||
|
|
Loading…
Reference in New Issue