angularrxjsangular2-observables

take(1) vs first()


I found a few implementation of AuthGuards that use take(1). In my project, I used first().

Do both work the same way?

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)
    }
}

Solution

  • Operators first() and take(1) aren't the same.

    The first() operator takes an optional predicate function and emits an error notification when no value matched when the source completed.

    For example this will emit an error:

    import { EMPTY, range } from 'rxjs';
    import { first, take } from 'rxjs/operators';
    
    EMPTY.pipe(
      first(),
    ).subscribe(console.log, err => console.log('Error', err));
    

    ... as well as this:

    range(1, 5).pipe(
      first(val => val > 6),
    ).subscribe(console.log, err => console.log('Error', err));
    

    While this will match the first value emitted:

    range(1, 5).pipe(
      first(),
    ).subscribe(console.log, err => console.log('Error', err));
    

    On the other hand take(1) just takes the first value and completes. No further logic is involved.

    range(1, 5).pipe(
      take(1),
    ).subscribe(console.log, err => console.log('Error', err));
    

    Then with empty source Observable it won't emit any error:

    EMPTY.pipe(
      take(1),
    ).subscribe(console.log, err => console.log('Error', err));
    

    Jan 2019: Updated for RxJS 6