238

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)
    }
}
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Calvin Ferrando
  • 3,794
  • 3
  • 16
  • 26

6 Answers6

317

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

martin
  • 93,354
  • 25
  • 191
  • 226
  • 4
    Just as note, I didn't say that `first()` and `take()` are the same in general, which I think is obvious, only that `first()` and `take(1)` are the same. I'm not sure from your answer if you think there is still a difference? – Günter Zöchbauer Feb 20 '17 at 13:47
  • 27
    @GünterZöchbauer Actually, their behavior is different. If the source doesn't emit anything and completes then `first()` send error notification while `take(1)` simply won't emit anything. – martin Feb 20 '17 at 13:49
  • @martin, at some cases take(1) won't emit anything means to say debugging the code will be harder? – Calvin Ferrando Feb 20 '17 at 13:54
  • 7
    @Karuban This really depends on your usecase. If not receiving any value is unexpected than I'd suggest to use `first()`. If it's a valid application state I'd go with `take(1)`. – martin Feb 20 '17 at 13:58
  • 3
    This is similar to .NET's `.First()` vs `.FirstOrDefault()` (and come to think of it also `.Take(1)` in that First requires something in the collection and gives an error for an empty collection - and both `FirstOrDefault()` and `.Take(1)` allow the collection to be empty and return `null` and empty collection respectively. – Simon_Weaver Jan 10 '19 at 07:20
85

Tip: Only use first() if:

  • You consider zero items emitted to be an error condition (eg. completing before emitting) AND if there’s a greater than 0% chance of error you handling it gracefully
  • OR You know 100% that the source observable will emit 1+ items (so can never throw).

If there are zero emissions and you are not explicitly handling it (with catchError) then that error will get propagated up, possibly cause an unexpected problem somewhere else and can be quite tricky to track down - especially if it's coming from an end user.

You're safer off using take(1) for the most part provided that:

  • You're OK with take(1) not emitting anything if the source completes without an emission.
  • You don't need to use an inline predicate (eg. first(x => x > 10) )

Note: You can use a predicate with take(1) like this: .pipe( filter(x => x > 10), take(1) ). There is no error with this if nothing is ever greater than 10.

What about single()

If you want to be even stricter, and disallow two emissions you can use single() which errors if there are zero or 2+ emissions. Again you'd need to handle errors in that case.

Tip: Single can occasionally be useful if you want to ensure your observable chain isn't doing extra work like calling an http service twice and emitting two observables. Adding single to the end of the pipe will let you know if you made such a mistake. I'm using it in a 'task runner' where you pass in a task observable that should only emit one value, so I pass the response through single(), catchError() to guarantee good behavior.


Why not always use first() instead of take(1) ?

aka. How can first potentially cause more errors?

If you have an observable that takes something from a service and then pipes it through first() you should be fine most of the time. But if someone comes along to disable the service for whatever reason - and changes it to emit of(null) or NEVER then any downstream first() operators would start throwing errors.

Now I realize that might be exactly what you want - hence why this is just a tip. The operator first appealed to me because it sounded slightly less 'clumsy' than take(1) but you need to be careful about handling errors if there's ever a chance of the source not emitting. Will entirely depend on what you're doing though.


If you have a default value (constant):

Consider also .pipe(defaultIfEmpty(42), first()) if you have a default value that should be used if nothing is emitted. This would of course not raise an error because first would always receive a value.

Note that defaultIfEmpty is only triggered if the stream is empty, not if the value of what is emitted is null.

Community
  • 1
  • 1
Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 4
    Be aware that `single` has more differences to `first`. **1.** It will only emit the value on `complete`. This means that if the observable emits a value but never completes then single will never emit a value. **2.** For some reason if you pass a filter function to `single` which does not match anything it will emit an `undefined` value if original sequence is not empty, which is not the case with `first`. – Marinos An Feb 18 '20 at 11:56
  • 1
    Regarding **2.** It was a [bug](https://github.com/ReactiveX/rxjs/issues/5320) that is now fixed. – Marinos An Mar 16 '21 at 09:16
59

Here are three Observables A, B, and C with marble diagrams to explore the difference between first, take, and single operators:

first vs take vs single operators comparison

* Legend:
--o-- value
----! error
----| completion

Play with it at https://thinkrx.io/rxjs/first-vs-take-vs-single/ .

Already having all the answers, I wanted to add a more visual explanation

Hope it helps someone

kos
  • 5,044
  • 1
  • 17
  • 35
  • does first() complete after receiving the first value? – Fernando Gabrieli Jan 28 '21 at 02:15
  • 1
    @FernandoGabrieli, yep! It completes immediately after emitting the first value. On the viz the `|` behind the `(0)` indicates that. More details at https://thinkrx.io/rxjs/first/ – kos Jan 29 '21 at 18:13
24

There's one really important difference which is not mentioned anywhere.

take(1) emits 1, completes, unsubscribes

first() emits 1, completes, but doesn't unsubscribe.

It means that your upstream observable will still be hot after first() which is probably not expected behavior.

UPD: This referes to RxJS 5.2.0. This issue might be already fixed.

norekhov
  • 3,915
  • 25
  • 45
  • I don't think either one unsubscribes, see http://jsbin.com/nuzulorota/1/edit?js,console. – Dennis Hackethal Jul 12 '17 at 04:51
  • 12
    Yes, both operators complete the subscription, the difference happens in the error handling. If that observable does not emit values and still try to take the first value using the first operator it will throw an error. If we replace it with take(1) operator even though value is not there in the stream when the subscription happens it does not throw an error. – noelyahan Sep 12 '17 at 09:25
  • 9
    To clarify: both do unsubscribe. The example from @weltschmerz was too simplified, it does not run until it could unsubscribe by itself. This one is a little more expanded: https://repl.it/repls/FrayedHugeAudacity – Stephan LV Mar 18 '18 at 20:13
13

It seems that in RxJS 5.2.0 the .first() operator has a bug,

Because of that bug .take(1) and .first() can behave quite different if you are using them with switchMap:

With take(1) you will get behavior as expected:

var x = Rx.Observable.interval(1000)
   .do( x=> console.log("One"))
   .take(1)
   .switchMap(x => Rx.Observable.interval(1000))
   .do( x=> console.log("Two"))
   .subscribe((x) => {})

// In the console you will see:
// One
// Two
// Two
// Two
// Two
// etc...

But with .first() you will get wrong behavior:

var x = Rx.Observable.interval(1000)
  .do( x=> console.log("One"))
  .first()
  .switchMap(x => Rx.Observable.interval(1000))
  .do( x=> console.log("Two"))
  .subscribe((x) => {})

// In console you will see:
// One
// One
// Two
// One
// Two
// One
// etc... 

Here's a link to codepen

Sergey
  • 1,608
  • 1
  • 27
  • 40
Artem
  • 691
  • 5
  • 20
11

It turns out there's a very important distinction between the two methods: first() will emit an error if the stream completes before a value is emitted. Or, if you've provided a predicate (i.e. first(value => value === 'foo')), it will emit an error if the stream completes before a value that passes the predicate is emitted.

take(1), on the other hand, will happily carry on if a value is never emitted from the stream. Here's a simple example:

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();

Another example, using a predicate:

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

As a newcomer to RxJS, this behavior was very confusing to me, although it was my own fault because I made some incorrect assumptions. If I had bothered to check the docs, I would have seen that the behavior is clearly documented:

Throws an error if defaultValue was not provided and a matching element is not found.

The reason I've run into this so frequently is a fairly common Angular 2 pattern where observables are cleaned up manually during the OnDestroy lifecycle hook:

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

The code looks harmless at first, but problems arise when the component in destroyed before stream$ can emit a value. Because I'm using first(), an error is thrown when the component is destroyed. I'm usually only subscribing to a stream to get a value that is to be used within the component, so I don't care if the component gets destroyed before the stream emits. Because of this, I've started using take(1) in almost all places where I would have previously used first().

filter(fn).take(1) is a bit more verbose than first(fn), but in most cases I prefer a little more verbosity over handling errors that ultimately have no impact on the application.

Also important to note: The same applies for last() and takeLast(1).

reference

Kamran Khatti
  • 3,754
  • 1
  • 20
  • 31