RxJS: The differences between first(), take(1), and single()
The different ways to get the first element of an Observable
With Observables listening for
mouseup events, implementing drag-and-drop requires knowing when the first
mouseup happens after a
mousedown event. For this, you need to get the first event in the
mouseup stream for every event coming in the
It turns out, there are more than one ways to do this, and they seemingly do the same. The
first() is the primary candidate for this use-case, but there is also
take(1) that also returns the first element. Then there is the
single() operator, which works a bit different but is also used to return the first element in some cases.
rxjs.of(1, 2, 3).pipe(rxjs.operators.first()) // 1 rxjs.of(1, 2, 3).pipe(rxjs.operators.take(1)) // 1 rxjs.of(1).pipe(rxjs.operators.single()) // 1
So, what is the difference between these operators?
Both of these return the first element in an Observable and they even have the same timing as they both complete the result after emitting the element.
The difference is when there are no elements in the input stream. In this case,
first() throws an Error, while
take(1) closes the Observable without any elements.
rxjs.of().pipe(rxjs.operators.first()) // EmptyError rxjs.of().pipe(rxjs.operators.take(1)) // no elements
When you are sure that the input Observable is not empty, it’s safer to use
first() as it will report the empty case as an error. On the other hand, if an empty stream can happen, for example the element for the
mouseup events is removed from the DOM,
take(1) is better.
This operator behaves a bit differently than the other two. It not only throws an Error for an empty stream (just like
first()) but also for one that has more than 1 element.
This also changes when it completes the result Observable as it needs to wait for the second element (or the end of the input stream) to know that there is indeed only one element coming in the input.
rxjs.of(1).pipe(rxjs.operators.single()) // 1 rxjs.of(1, 2, 3).pipe(rxjs.operators.single()) // Sequence contains more than one element rxjs.of().pipe(rxjs.operators.single()) // EmptyError
single() operator is a safer version of
first() if you want to make sure that only a single element is emitted in the input Observable.
The predicate and defaultValue arguments
first() and the
single() operators also support the predicate argument to filter the elements. Apart from this,
first() also supports the defaultValue that it returns in case of an empty Observable.
take(1) supports neither.
// predicate rxjs.of(1, 2, 3).pipe(rxjs.operators.first((e) => e % 2 === 0)) // 2 // defaultValue rxjs.of().pipe(rxjs.operators.first(undefined, 5)) // 5
These are merely syntactic sugar that you can emulate with the
filter and the
rxjs.of(1, 2, 3).pipe( rxjs.operators.filter((e) => e % 2 === 0), rxjs.operators.first(), ) // 2 rxjs.of().pipe( rxjs.operators.defaultIfEmpty(5), rxjs.operators.first(), ) // 5
In the case of
single(), providing a filter changes how it reacts to empty streams. If there are no elements in the Observable then it emits an Error. But if there are elements but they are filtered out, it emits
first() operator emits an Error for an empty Observable, while
take(1) emits nothing.
single() operator also emits an Error for the second element in the Observable. This also changes the time it emits its result as it needs to wait for the end of the stream (or the second element).