take(1) vs first()

190,239

Solution 1

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

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

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

Share:
190,239

Related videos on Youtube

Calvin Ferrando
Author by

Calvin Ferrando

BY DAY: I spend my time sleeping. BY NIGHT: I code 'til my hands bleed. FOR FUN: I want to learn new stuff or just enhance my coding skills.

Updated on July 21, 2022

Comments

  • Calvin Ferrando
    Calvin Ferrando almost 2 years

    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)
        }
    }
    
  • Günter Zöchbauer
    Günter Zöchbauer about 7 years
    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?
  • martin
    martin about 7 years
    @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.
  • Calvin Ferrando
    Calvin Ferrando about 7 years
    @martin, at some cases take(1) won't emit anything means to say debugging the code will be harder?
  • martin
    martin about 7 years
    @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).
  • Dennis Hackethal
    Dennis Hackethal almost 7 years
    I don't think either one unsubscribes, see jsbin.com/nuzulorota/1/edit?js,console.
  • noelyahan
    noelyahan over 6 years
    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.
  • Stephan LV
    Stephan LV about 6 years
    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: repl.it/repls/FrayedHugeAudacity
  • aruno
    aruno over 5 years
    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.
  • Marinos An
    Marinos An about 4 years
    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.
  • Fernando Gabrieli
    Fernando Gabrieli over 3 years
    does first() complete after receiving the first value?
  • kos
    kos over 3 years
    @FernandoGabrieli, yep! It completes immediately after emitting the first value. On the viz the | behind the (0) indicates that. More details at thinkrx.io/rxjs/first
  • Marinos An
    Marinos An about 3 years
    Regarding 2. It was a bug that is now fixed.