RxJS: The differences between first(), take(1), and single()

The different ways to get the first element of an Observable

Author's image
Tamás Sallai
3 mins

With Observables listening for mousedown and 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 mousedown.

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?

first() vs take(1)

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.

12first()1take(1)1

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

first()take(1)

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.

single()

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.

12first()1single()

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.

1first()1single()1

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

The 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

The 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 defaultIfEmpty operators:

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 undefined instead.

Conclusion

The first() operator emits an Error for an empty Observable, while take(1) emits nothing.

The 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).

22 December 2020