diff --git a/aio/content/guide/service-worker-devops.md b/aio/content/guide/service-worker-devops.md index 1935a2d9c0..6af45fdd7d 100644 --- a/aio/content/guide/service-worker-devops.md +++ b/aio/content/guide/service-worker-devops.md @@ -32,12 +32,10 @@ server can ensure that the Angular app always has a consistent set of files. #### Update checks -Every time the Angular service worker starts, it checks for updates to the -app by looking for updates to the `ngsw.json` manifest. - -Note that the service worker starts periodically throughout the usage of -the app because the web browser terminates the service worker if the page -is idle beyond a given timeout. +Every time the user opens or refreshes the application, the Angular service worker +checks for updates to the app by looking for updates to the `ngsw.json` manifest. If +an update is found, it is downloaded and cached automatically, and will be served +the next time the application is loaded. ### Resource integrity @@ -276,8 +274,8 @@ with service workers. Such tools can be powerful when used properly, but there are a few things to keep in mind. * When using developer tools, the service worker is kept running -in the background and never restarts. For the Angular service -worker, this means that update checks to the app will generally not happen. +in the background and never restarts. This can cause behavior with Dev +Tools open to differ from behavior a user might experience. * If you look in the Cache Storage viewer, the cache is frequently out of date. Right click the Cache Storage title and refresh the caches. diff --git a/packages/service-worker/worker/src/driver.ts b/packages/service-worker/worker/src/driver.ts index 6d2f229190..755e9e4a29 100644 --- a/packages/service-worker/worker/src/driver.ts +++ b/packages/service-worker/worker/src/driver.ts @@ -88,6 +88,11 @@ export class Driver implements Debuggable, UpdateSource { private lastUpdateCheck: number|null = null; + /** + * Whether there is a check for updates currently scheduled due to navigation. + */ + private scheduledNavUpdateCheck: boolean = false; + /** * A scheduler which manages a queue of tasks that need to be executed when the SW is * not doing any other work (not processing any other requests). @@ -327,6 +332,15 @@ export class Driver implements Debuggable, UpdateSource { return this.safeFetch(event.request); } + // On navigation requests, check for new updates. + if (event.request.mode === 'navigate' && !this.scheduledNavUpdateCheck) { + this.scheduledNavUpdateCheck = true; + this.idle.schedule('check-updates-on-navigation', async() => { + this.scheduledNavUpdateCheck = false; + await this.checkForUpdate(); + }); + } + // Decide which version of the app to use to serve this request. This is asynchronous as in // some cases, a record will need to be written to disk about the assignment that is made. const appVersion = await this.assignVersion(event); diff --git a/packages/service-worker/worker/test/happy_spec.ts b/packages/service-worker/worker/test/happy_spec.ts index 14b404926b..e578b8aeb7 100644 --- a/packages/service-worker/worker/test/happy_spec.ts +++ b/packages/service-worker/worker/test/happy_spec.ts @@ -337,6 +337,41 @@ export function main() { serverUpdate.assertNoOtherRequests(); }); + async_it('checks for updates on navigation', async() => { + expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo'); + await driver.initialized; + server.clearRequests(); + + expect(await makeRequest(scope, '/foo.txt', 'default', { + mode: 'navigate', + })).toEqual('this is foo'); + + scope.advance(12000); + await driver.idle.empty; + + server.assertSawRequestFor('ngsw.json'); + }); + + async_it('does not make concurrent checks for updates on navigation', async() => { + expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo'); + await driver.initialized; + server.clearRequests(); + + expect(await makeRequest(scope, '/foo.txt', 'default', { + mode: 'navigate', + })).toEqual('this is foo'); + + expect(await makeRequest(scope, '/foo.txt', 'default', { + mode: 'navigate', + })).toEqual('this is foo'); + + scope.advance(12000); + await driver.idle.empty; + + server.assertSawRequestFor('ngsw.json'); + server.assertNoOtherRequests(); + }); + async_it('preserves multiple client assignments across restarts', async() => { expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo'); await driver.initialized;