RxJS Mastery – #51 take

The RxJS take operators take some values based on a count, notifier Observable, or predicate. Besides the basic take, it comes in additional flavors like takeLast, takeUntil & takeWhile. Take is one of the most used operators because it is a simple way to unsubscribe from the source.

RxJS take emits the first n values

The take operator emits values from the source until a certain count is reached. If the source emits fewer values than the specified count, all values are emitted. As soon as the count is reached (or all values are emitted) it completes:

const result$ = interval(1).pipe(
    take(3)
);

expectObservable(result$).toBe(
    '-ab(c|)',
    { a: 0, b: 1, c: 2 }
);

Compared to first(), the take(1) operator is not throwing an error if no values are arriving. Hence, the below code is safe:

const result$ = EMPTY.pipe(
    take(1),
);

expectObservable(result$).toBe('|', null);

RxJS takeLast waits until completion and emits the last n values

As the name suggests, takeLast considers the last n values. To know the last n values it has to wait until the source completes. This also means the values in the end are delivered at the same time (together with the complete notification):

const result$ = interval(1).pipe(
    take(5),
    takeLast(2),
);

expectObservable(result$).toBe(
    '-----(ab|)',
    { a: 3, b: 4 }
);

If the source completes and the count cannot be reached, all values are emitted. Special attention needs to be paid to Observables that never complete. In this case, takeLast is not able to determine the last values and is not emitting any value.

RxJS takeUntil emits values until a notifier emits

Also takeUntil can be seen a lot in some code bases, e.g. in the form of takeUntil(onDestroy$). This operator emits values until a notifier emits. After that the operator unsubscribes from the source.

const notifier$ = cold('---r', { r: 'response' });

const result$ = interval(1).pipe(
    takeUntil(notifier$)
);

expectObservable(result$).toBe(
    '-ab|',
    { a: 0, b: 1 }
);

You could for example run a loading spinner Observable until a response has arrived.

If the notifier$ just completes without emitting any values, the source will not stop and deliver all values:

const notifier$ = cold('-|');

const result$ = interval(1).pipe(
    take(5),
    takeUntil(notifier$)
);

expectObservable(result$).toBe(
    '-abcd(e|)',
    { a: 0, b: 1, c: 2, d: 3, e: 4 }
);

RxJS takeWhile emits values while a condition holds true

Finally, there is the takeWhile variant that accepts a condition. As long as this condition is true, values are emitted. Once it became false the first time the stream completes:

const result$ = interval(1).pipe(
    takeWhile(x => x < 3)
);

expectObservable(result$).toBe(
    '-abc|',
    { a: 0, b: 1, c: 2 }
);

Exercise for the RxJS take operator 💪

Demonstrate in a test that the take operator actually unsubscribes from the source. This gives you a starting point:

const source$ = cold('0123456789');
const result$ = source$.pipe(
    take(3)
);

This post is part of the RxJS mastery series. As always the code examples can be found on GitHub.