From afe45616ad2fc4edd5cba84a233ea3a76a2c09b7 Mon Sep 17 00:00:00 2001 From: arturovt Date: Tue, 16 Apr 2024 19:10:47 +0300 Subject: [PATCH] fix(service-worker): remove `controllerchange` listener when app is destroyed (#55365) This commit updates the `ngswAppInitializer` implementation and removes the `controllerchange` listener upon the destruction of the `ApplicationRef`. This adjustment aims to prevent memory leaks. In a zone.js environment, neglecting to do so could lead to the perpetual creation of a zone task, which captures the zone and obstructs proper garbage collection. PR Close #55365 --- packages/service-worker/src/provider.ts | 12 ++++++++---- packages/service-worker/test/provider_spec.ts | 12 ++++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/service-worker/src/provider.ts b/packages/service-worker/src/provider.ts index b06011c85c8..0ef2453c646 100644 --- a/packages/service-worker/src/provider.ts +++ b/packages/service-worker/src/provider.ts @@ -40,6 +40,7 @@ export function ngswAppInitializer( } const ngZone = injector.get(NgZone); + const appRef = injector.get(ApplicationRef); // Set up the `controllerchange` event listener outside of // the Angular zone to avoid unnecessary change detections, @@ -48,10 +49,13 @@ export function ngswAppInitializer( // Wait for service worker controller changes, and fire an INITIALIZE action when a new SW // becomes active. This allows the SW to initialize itself even if there is no application // traffic. - navigator.serviceWorker.addEventListener('controllerchange', () => { - if (navigator.serviceWorker.controller !== null) { - navigator.serviceWorker.controller.postMessage({action: 'INITIALIZE'}); - } + const sw = navigator.serviceWorker; + const onControllerChange = () => sw.controller?.postMessage({action: 'INITIALIZE'}); + + sw.addEventListener('controllerchange', onControllerChange); + + appRef.onDestroy(() => { + sw.removeEventListener('controllerchange', onControllerChange); }); }); diff --git a/packages/service-worker/test/provider_spec.ts b/packages/service-worker/test/provider_spec.ts index f6d8f869a8c..0b4367b221e 100644 --- a/packages/service-worker/test/provider_spec.ts +++ b/packages/service-worker/test/provider_spec.ts @@ -168,7 +168,11 @@ const serviceWorkerModuleApi = 'ServiceWorkerModule'; provideServiceWorker('sw.js'), { provide: ApplicationRef, - useValue: {isStable: isStableSub.asObservable(), afterTick: new Subject()}, + useValue: { + isStable: isStableSub.asObservable(), + afterTick: new Subject(), + onDestroy: () => {}, + }, }, {provide: PLATFORM_ID, useValue: 'browser'}, { @@ -183,7 +187,11 @@ const serviceWorkerModuleApi = 'ServiceWorkerModule'; providers: [ { provide: ApplicationRef, - useValue: {isStable: isStableSub.asObservable(), afterTick: new Subject()}, + useValue: { + isStable: isStableSub.asObservable(), + afterTick: new Subject(), + onDestroy: () => {}, + }, }, {provide: PLATFORM_ID, useValue: 'browser'}, {