take(1) vs first()
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:
* 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
Related videos on Youtube
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, 2022Comments
-
Calvin Ferrando almost 2 years
I found a few implementation of
AuthGuard
s that usetake(1)
. In my project, I usedfirst()
.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 about 7 yearsJust as note, I didn't say that
first()
andtake()
are the same in general, which I think is obvious, only thatfirst()
andtake(1)
are the same. I'm not sure from your answer if you think there is still a difference? -
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 whiletake(1)
simply won't emit anything. -
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 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 withtake(1)
. -
Dennis Hackethal almost 7 yearsI don't think either one unsubscribes, see jsbin.com/nuzulorota/1/edit?js,console.
-
noelyahan over 6 yearsYes, 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 about 6 yearsTo 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 over 5 yearsThis 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 bothFirstOrDefault()
and.Take(1)
allow the collection to be empty and returnnull
and empty collection respectively. -
Marinos An about 4 yearsBe aware that
single
has more differences tofirst
. 1. It will only emit the value oncomplete
. 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 tosingle
which does not match anything it will emit anundefined
value if original sequence is not empty, which is not the case withfirst
. -
Fernando Gabrieli over 3 yearsdoes first() complete after receiving the first value?
-
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 about 3 yearsRegarding 2. It was a bug that is now fixed.