JavaScript and Memory Leaks
Tempo di lettura: 4 minuti

Anche se presti attenzione a ripulire gli abbonamenti quando i componenti vengono distrutti, c’è ancora il rischio di perdite di memoria dagli abbonamenti RxJS annidati. In effetti, ci sono anche altri problemi con gli abbonamenti nidificati, come i problemi di temporizzazione. Per questi motivi, gli abbonamenti nidificati hanno guadagnato il primo posto nell’elenco degli anti-pattern RxJS. Molti sviluppatori usano ancora gli abbonamenti nidificati semplicemente perché non sanno come fare rifattorizzare loro per evitare la nidificazione. È un problema complesso, per il quale non esiste una soluzione valida per tutti. In effetti, la strada per una migliore gestione degli abbonamenti RxJS è studiare numerosi esempi di utilizzo “corretto”. Sfortunatamente, i buoni esempi scarseggiano. Pertanto, ne aggiungeremo un altro alla pila in questo tutorial. In questo caso, prenderemo del codice reale (anche se leggermente riscritto per essere più generico) le cui sottoscrizioni nidificate risultano da condizioni e lo riscriviamo senza l’annidamento.

Procedura dettagliata del codice

Il codice che esamineremo qui oggi proviene da un’applicazione reale che raccoglie articoli di notizie, post sui social media e singoli blog pubblicati online su azioni e altri strumenti di investimento. L’applicazione fornisce quindi analisi che consentono agli investitori di ottenere una visione affidabile di la follal’impatto sulle loro partecipazioni.

Il codice in questione recupera i dati per la schermata del cruscotto, nella foto sopra:

this.userInfoService.userPermissions.subscribe((permissions) => {
  this.isLimited = permissions.preLogin;
  if (!this.isLimited) {
    this.trackerService.recordPageView(PageViewCategory.dashboard);

    this.dashboardService
      .takeUntil(this.ngUnsubscribe)
      .subscribe((e) => {
        const id = e.entityId;
        if (id) {
          this.apiService.getArticleStats(id).subscribe(
            (article) => {
              if (article != null) {
                this.entity = article.entity;
                // Set current entity for dashboard page
                this.dashboardService.setCurrentEntity(article.entity);
                if (this.entity?.instrumentId) {
                  this.loadInstrumentData(this.entity.instrumentId);
                } 
              }
            }
          );
        }
      });

Ce ne sono tre SE dichiarazioni che determinano se sottoscrivere o meno un altro servizio:

  1. Se l’accesso dell’utente non è limitato
  2. Se l’entità (e) ha un identità
  3. Se l’articolo ha un’entità associata

Eliminare le dichiarazioni IF

Il flusso di controllo del codice sopra non è diverso da questo esempio che mi sono imbattuto su stackoverflow.com, che ha anche sottoscritto più servizi in base al risultato di un’istruzione IF:

source.subscribe(x => {
    doAnyway(x);
    if (x.Id) {             
        doSometing(x);
    } else {
        // id Not set, get default Id
        this.idService.getDefault().subscribe(id => {
            x.Id = id;                  
            doSometing(x);
        });
    }
});

In quel caso, un intervistato ha suggerito la seguente struttura che impiega l’RxJS rubinetto() e switchMap() operatori per sostituire la struttura del codice precedente:

source.pipe(
  tap(val => doAnyway(val)),
  switchMap(val => val.id ? of(val.id) : this.idService.getDefault())
).subscribe(id => {
  this.id = id;
  doSomething(id);
});

A quanto pare, questi due operatori funzioneranno altrettanto bene per noi!

RxJS tap() e switchMap() spiegato

Come suggerisce il nome, rubinetto() esegue un effetto collaterale per ogni emissione sulla sorgente Observable, e restituisce un Observable identico alla sorgente. Quindi, è perfetto per fare qualcosa con un osservabile in cui non si desidera alterarlo in alcun modo.

Possiamo usare rubinetto() per impostare il è limitato bandiera come segue:

this.userInfoService.userPermissions
    .pipe(
      takeUntil(this.ngUnsubscribe),
      tap(permissions => this.isLimited = permissions.preLogin)
    )
    .subscribe((article) => {
      // etc...
    });

Passando a switchMap(), è una delle numerose funzioni di mappatura RxJS che funzionano ciascuna in modo leggermente diverso. Il switchMap() La funzione crea un osservabile derivato (chiamato osservabile interno) da un osservabile di origine ed emette quei valori. Ogni volta che la sorgente emette un nuovo valore, switchMap() creerà un nuovo osservabile interno e passerà invece a quei valori. Gli osservabili interni che vengono creati al volo vengono annullati dalla sottoscrizione, lasciando l’osservabile di origine aperto per emettere più valori.

Il trucco per usare le funzioni di mappatura è che dobbiamo restituire l’osservabile successivo nella catena. Nel nostro caso, dobbiamo passare il dashboardService insieme per il prossimo passo. Se l’utente ha un accesso limitato, possiamo restituire un vuoto Parametri Dashboard oggetto e creare un nuovo osservabile da esso utilizzando RxJS di() funzione:

this.userInfoService.userPermissions
    .pipe(
      takeUntil(this.ngUnsubscribe),
      tap(permissions => this.isLimited = permissions.preLogin)
      ),
      switchMap(() => {
        if (this.isLimited) {
          return of(new DashboardParams());
        } else {
          this.trackerService.recordPageView(PageViewCategory.dashboard);
  
          return this.dashboardService
        }
      })
    )
    .subscribe((article) => {
      // etc...
    });

Dobbiamo ancora chiamare il apiService per ottenere i dati dell’articolo, quindi ora dobbiamo elaborare il Parametri Dashboard e restituire il apiService.getArticleStats’s emesso Osservabile. Ancora, switchMap() è la funzione per farlo. Se l’Entità emessa (e) ha un identità, possiamo quindi procedere alla chiamata apiService.getArticleStats(). Altrimenti, possiamo restituire di nuovo un osservabile che emette un vuoto Dati articolo oggetto, che è apiService.getArticleStats()‘ tipo emesso. Infine, i dati dell’articolo vengono elaborati nel sottoscrivi() gestore:

this.userInfoService.userPermissions
  .pipe(
    takeUntil(this.ngUnsubscribe),
    tap((permissions) => {
      this.isLimited = permissions.preLogin;
    }),
    switchMap(() => {
      if (this.isLimited) {
        return of(new DashboardParams());
      } else {
        this.trackerService.recordPageView(PageViewCategory.dashboard);

        return this.dashboardService
      }
    }),
    switchMap((e: DashboardParams) => {
      const id = e.entityId;
      return id 
        ? this.apiService.getArticleStats(id) 
        : of(<ArticleData>{})
    })
  )
  .subscribe((article) => {
    if (article != null) {
      this.entity = article.entity;
      // Set current entity for dashboard page
      this.dashboardService.setCurrentEntity(article.entity);
      if (this.entity.instrumentId) {
        this.loadInstrumentData(this.entity.instrumentId);
      } 
    }
  });

Conclusione

Con circa un miliardo di operatori, RxJS è senza dubbio lo strumento più potente per lavorare con codice asincrono o basato su callback. È anche il più complicato. Come accennato nell’introduzione, il refactoring degli abbonamenti nidificati non è facile, poiché ogni caso deve essere valutato in base ai propri requisiti individuali. Si spera che l’esempio presentato qui oggi aiuti a strutturare il proprio codice in un modo più corretto ed efficiente.

Source link

Di Simone Serra

Web Designer Freelancer Realizzazione Siti Web Serra Simone Realizzo siti web, portali ed e-commerce con focus specifici sull’usabilità, l’impatto grafico, una facile gestione e soprattutto in grado di produrre conversioni visitatore-cliente. Elaboro siti internet, seguendo gli standard Web garantendo la massima compatibilità con tutti i devices. Sviluppo e-commerce personalizzati, multilingua, geolocalizzati per potervi mettere nelle migliori condizioni di vendita. Posiziono il tuo sito su Google per dare maggiore visibilità alla tua attività sui motori di ricerca con SEO di base o avanzato.