Angular PWA: Using Service Worker to do a scheduled job or async task with page/web-app

38 views Asked by At

Am new to angular service workers, and have the following needs

  • Want to have a separate thread than the one in the app taking care of purging every n minutes an indexedDb containing stuff, and notify the tab/tabs that this has been done
  • Would like that separate service/job to take care of doing some background calls that may fail and if so being retried till done, like posting photos to an API.

Am wondering which way to take for that with angular, the only approach I can see so far is having a PwaService injected into app.component.ts and then call the init() method, but seems to me its gonna just be yet another thing/component of my ng application

export class PwaService {
  constructor(
    private update: SwUpdate,
    private appRef: ApplicationRef
  ) {}

  init(): void {
    if (!this.update.isEnabled) {
      return;
    }
    console.info('Auto update is enabled');

    const appIsStable$ = this.appRef.isStable.pipe(first((isStable) => isStable === true));
    const everyHour$ = interval(60 * 60 * 1000);

    concat(appIsStable$, everyHour$).subscribe(() => {
      //do some scheduled job making sure its not done by ng application and blocking DOM and stuff...
    });
  }
}

app.component.ts

constructor(private pwaService: PwaService){this.pwaService.init();}

BTW I tested the above implementation with a loop and its blocking the DOM in the meantime... so that's probably not the way of doing things

1

There are 1 answers

1
Ricardo Gellman On BEST ANSWER

You have to separate taks into a Web Worker for IndexedDB purging and use RxJS for background API calls with retry logic in Angular,

Create a worker indexed-db-purge.worker.ts

addEventListener('message', ({ data }) => {
  if (data === 'purge') {
    purgeIndexedDB();
  }
});

function purgeIndexedDB() {
  try {
     ///Your code to purge IndexedDB
  } catch(error) {
  }
}

Create a service pwa.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError, timer } from 'rxjs';
import { retryWhen, mergeMap, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class PwaService {
  constructor(private http: HttpClient) {}

  purgeIndexedDB(): void {
    const worker = new Worker('./indexed-db-purge.worker', { type: 'module' });
    worker.postMessage('purge');
  }

  backgroundAPICalls(): void {
    timer(0, 60000) 
      .pipe(
        mergeMap(() => this.http.post<any>('your-api-endpoint', {})),
        retryWhen(errors => errors.pipe(
          mergeMap((error, index) => {
            if (index < 3) {
              return timer(5000); // 5 seconds
            }
            return throwError(error);
          }),
        )),
        catchError(error => {
          console.error('API call failed:', error);
          return throwError(error);
        })
      )
      .subscribe(response => {
        console.log('API call successful:', response);
      });
  }
}

Call the services:

import { Component, OnInit } from '@angular/core';
import { PwaService } from './pwa.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private pwaService: PwaService) {}

  ngOnInit(): void {
    this.pwaService.purgeIndexedDB();
    this.pwaService.backgroundAPICalls();
  }
}