RxJS Mastery – #49 single

RxJS Mastery lesson title single operator

The RxJS single operator asserts that only one value is emitted from the source Observable. The operator also accepts a predicate. So, in the predicate case, it makes sure a single value is emitted that matches the predicate. If the single value assertion is violated an error is thrown.

RxJS single emits a single value

The RxJS single operator should be used to make your code more robust. Often it’s beneficial to assert certain things and fail if those assertions do not hold. Therefore, the single operator can be used to assert that you only expect a single value from the source (matching the predicate).

Let’s take the example below where we have a single value a that is emitted. We know that the of operator completes immediately. So, one single value was emitted and the source was completed. This means the assertion is fulfilled and the value is emitted in the final output.

const result$ = of('a').pipe(single());

expectObservable(result$).toBe('(a|)');

RxJS single accepts a predicate

When we want to check that only a single value fulfilling a condition is emitted, then we pass a predicate to the single operator. In the below example some characters (a, b, c) are emitted. In the predicate, we specify that we’re only interested in b.

const result$ = cold('abc|').pipe(single(v => v === 'b'));

expectObservable(result$).toBe('---(b|)');

As expected we do not see any error in the final Observable, but the single value b. Importantly, the b is emitted by the source at frame number 2. But because the single operator needs to wait until the source has been completed, the value in the result$ Observable is emitted in a later frame together with the completion notification. We see that, compared to e.g. first, the single operator always needs to wait until the stream completes.

RxJS single throws a SequenceError if multiple values are emitted

In the above examples everything was fine and the assertion was evaluated to true. As soon as more than one value is emitted, a SequenceError is thrown:

const result$ = of('a', 'b').pipe(single());

expectObservable(result$).toBe(
    '#', null,
    new SequenceError('Too many matching values')
);

RxJS single throws an EmptyError if no value is emitted

Similar to multiple values, the operator also throws an error if no value at all is emitted and the source Observable completes. In this case an EmptyError is the result:

const result$ = EMPTY.pipe(single());

expectObservable(result$).toBe(
    '#', null, 
    new EmptyError()
);

Exercise for the RxJS single operator 💪

Use the single operator to output the winner ranking object:

const winner$ = from([
    { rank: 2, team: 'Team Blue' },
    { rank: 1, team: 'Team Red' },
    { rank: 3, team: 'Team Yellow' },
]);

expectObservable(winner$).toBe('(w|)', { w: { rank: 1, team: 'Team Red'}});

As always the code examples can be found on GitHub.