I recently came across MkDocs-Material by Martin Donath, a fantastic open-source project with over 22k GitHub stars.
It’s an incredible contribution to the community, making documentation hosting effortless.
While exploring it, I got curious about how such a large project achieves reactiveness.
The stack is mostly HTML, SCSS, Preact, RxJS, and a few workers, and I saw this as the perfect opportunity to dive into RxJS—especially how it utilizes Observables and other advanced patterns.
RxJS offers a wide variety of operators that help in handling observables efficiently.
Among them, the first
and last
operators allow you to pick specific values from an observable stream.
Understanding these operators can be crucial when dealing with data streams where only the first or last emitted value matters.
The first
Operator: Picking the First Emission
The first
operator extracts only the very first value emitted by an observable and then completes.
Example:
import { of, first } from 'rxjs';
const numbers$ = of(-3, -2, -1, 0, 1, 2, 3);
numbers$.pipe(
first()
).subscribe(value => console.log(value));
// Output: -3
In this case, first()
ensures that only -3
, the first value emitted by numbers$
, is logged.
Applying Conditions
You can also pass a predicate function to first()
, which selects the first value that satisfies a condition:
numbers$.pipe(
first(value => value > 0)
).subscribe(value => console.log(value));
// Output: 1
The last
Operator: Picking the Last Emission
The last
operator extracts only the last emitted value before the observable completes.
Example:
import { last } from 'rxjs';
numbers$.pipe(
last()
).subscribe(value => console.log(value));
// Output: 3
Here, last()
ensures that only 3
, the last value from numbers$
, is logged.
Applying Conditions
Just like first()
, you can pass a condition to last()
:
numbers$.pipe(
last(value => value < 0)
).subscribe(value => console.log(value));
// Output: -1
Real-World Use Cases
- Fetching API data: If an API response contains an array of data and you only need the first or last item.
- Processing user input: Selecting the first user interaction from a stream of clicks.
- Handling data streams: Picking the first or last reading from a series of sensor measurements.
Bonus: Using forkJoin
for Combining Observables
If you need to combine multiple observables and only emit the final values, forkJoin
is your go-to operator.
Example:
import { forkJoin, of, delay } from 'rxjs';
const obs1$ = of('rainbows').pipe(delay(2000));
const obs2$ = of('unicorns').pipe(delay(2000));
forkJoin([obs1$, obs2$]).subscribe(values => console.log(values));
// Output (after 2s): ['rainbows', 'unicorns']
This ensures that the subscription only receives values after both observables have completed.
Handling Errors with catchError
and retry
Error handling is essential in reactive programming. The catchError
operator allows graceful handling of errors, while retry
lets you reattempt a failed observable sequence.
Example:
import { throwError, catchError, retry } from 'rxjs';
const faulty$ = throwError(() => new Error('Something went wrong!'));
faulty$.pipe(
retry(2),
catchError(err => of(`Error caught: ${err.message}`))
).subscribe(value => console.log(value));
// Output (after 2 retries): Error caught: Something went wrong!
Conclusion
The first
and last
operators are simple yet powerful tools for extracting key values from an observable stream.
Whether you're handling API responses, processing user interactions, or managing error scenarios, these operators come in handy.
Mastering them, along with forkJoin
, catchError
, and retry
, ensures you can build robust, efficient reactive applications.
I’ll be sharing more learnings, so stick around/follow for more deep dives into RxJS and beyond! 🚀
While exploring mkdocs-material implementation, I've been learning how to adapt these techniques for LiveAPI, a product I've been passionately working on for quite a while.
With LiveAPI, you can quickly generate interactive API documentation that allows users to execute APIs directly from the browser.
If you’re tired of manually creating docs for your APIs, this tool might just make your life easier.
Top comments (0)