226 votes

Angular 2 avec RxJS - take(1) vs first()

J'ai trouvé quelques implémentations de Auth Guards qui utilisent take(1) . Sur mon projet, j'ai utilisé first() pour satisfaire mes besoins. Est-ce que cela fonctionne de la même manière ? Ou l'un d'entre eux pourrait avoir des avantages.

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';
import { Observable } from 'rxjs/Observable';

import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AngularFire } from 'angularfire2';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(private angularFire: AngularFire, private router: Router) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
        return this.angularFire.auth.map(
            (auth) =>  {
                if (auth) {
                    this.router.navigate(['/dashboard']);
                    return false;
                } else {
                    return true;
                }
            }
        ).first(); // Just change this to .take(1)
    }
}

9voto

Kamran Khatti Points 367

Il s'avère qu'il y a une distinction très importante entre les deux méthodes : premier() émettra une erreur si le flux se termine avant qu'une valeur soit émise. Ou, si vous avez fourni un prédicat (i.e. first(value => value === 'foo')) il émettra une erreur si le flux se termine avant qu'une valeur qui passe le prédicat soit émise.

prendre(1) d'autre part, continuera à fonctionner si une valeur n'est jamais émise par le flux. Voici un exemple simple :

const subject$ = new Subject();

// logs "no elements in sequence" when the subject completes
subject$.first().subscribe(null, (err) => console.log(err.message));

// never does anything
subject$.take(1).subscribe(console.log);

subject$.complete();

Un autre exemple, l'utilisation d'un prédicat :

const observable$ = of(1, 2, 3);

// logs "no elements in sequence" when the observable completes
observable$
 .first((value) => value > 5)
 .subscribe(null, (err) => console.log(err.message));

// the above can also be written like this, and will never do
// anything because the filter predicate will never return true
observable$
 .filter((value) => value > 5);
 .take(1)
 .subscribe(console.log);

En tant que nouveau venu dans RxJS, ce comportement m'a beaucoup dérouté, bien que ce soit de ma propre faute car j'ai fait des hypothèses incorrectes. Si j'avais pris la peine de consulter la documentation, j'aurais vu que le comportement est le suivant clairement documenté :

Lance une erreur si defaultValue n'a pas été fourni et un élément correspondant n'est pas trouvé.

La raison pour laquelle j'ai rencontré ce problème si fréquemment est un modèle Angular 2 assez commun où les observables sont nettoyés manuellement pendant le processus d'exécution de la commande OnDestroy le crochet du cycle de vie :

class MyComponent implements OnInit, OnDestroy {
  private stream$: Subject = someDelayedStream();
  private destroy$ = new Subject();

  ngOnInit() {
    this.stream$
      .takeUntil(this.destroy$)
      .first()
      .subscribe(doSomething);
  }

  ngOnDestroy() {
    this.destroy$.next(true);
  }
}

Le code semble inoffensif au premier abord, mais des problèmes surviennent lorsque le composant est détruit avant que stream$ peut émettre une valeur. Parce que j'utilise first() Si je m'abonne à un flux pour obtenir une valeur à utiliser dans le composant, une erreur est déclenchée lorsque le composant est détruit. En général, je ne m'abonne à un flux que pour obtenir une valeur qui sera utilisée dans le composant, donc je ne me soucie pas que le composant soit détruit avant l'émission du flux. Pour cette raison, j'ai commencé à utiliser take(1) dans presque tous les endroits où j'aurais utilisé auparavant first() .

filter(fn).take(1) est un peu plus verbeux que first(fn) mais dans la plupart des cas, je préfère un peu plus de verbosité pour gérer des erreurs qui n'ont finalement aucun impact sur l'application.

Il est également important de noter que la même chose s'applique pour last() y takeLast(1) .

référence

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X