RxJS (Reactive Extensions for JavaScript) is a library for managing asynchronous streams of data. A key feature of RxJS is the ability to combine multiple Observables into a single Observable. One of the simplest and most powerful ways to achieve this is using the concat()
operator.
In this article, we’ll explore how concat()
works, its use cases, and examples to demonstrate its functionality.
What is concat()
?
The concat()
operator in RxJS sequentially combines multiple Observables into a single Observable. It subscribes to each Observable in order, waits for the current Observable to complete before subscribing to the next one, and emits all the values in sequence.
Key Characteristics
- Sequential Execution: Observables are executed one after the other.
- Order Preservation: Values are emitted in the same order as the Observables are passed.
- Completion-Driven: The next Observable starts only after the previous one completes.
Syntax
typescriptCopy codeconcat(input1: Observable, input2: Observable, ...): Observable
- Parameters: A list of input Observables.
- Returns: A new Observable that emits all the values from the input Observables sequentially.
Importing concat()
You can use concat()
from the rxjs
package:
typescriptCopy codeimport { concat } from 'rxjs';
How concat()
Works
Basic Flow
concat()
takes multiple Observables as arguments.- It subscribes to the first Observable and emits its values.
- When the first Observable completes, it subscribes to the next Observable and emits its values.
- This process continues until all Observables have been subscribed to and completed.
Examples of Using concat()
Example 1: Simple Concatenation
Combine two Observables that emit numeric values.
typescriptCopy codeimport { of, concat } from 'rxjs';
const observable1 = of(1, 2, 3);
const observable2 = of(4, 5, 6);
const result = concat(observable1, observable2);
result.subscribe((value) => console.log(value));
// Output:
// 1
// 2
// 3
// 4
// 5
// 6
Explanation
observable1
emits1, 2, 3
and completes.- After
observable1
completes,observable2
starts and emits4, 5, 6
.
Example 2: Delayed Observables
Concatenate Observables with time delays.
typescriptCopy codeimport { of, concat, delay } from 'rxjs';
const observable1 = of('A').pipe(delay(1000)); // Emits 'A' after 1 second
const observable2 = of('B', 'C').pipe(delay(500)); // Emits 'B', 'C' after 500ms
const result = concat(observable1, observable2);
result.subscribe((value) => console.log(value));
// Output (timed):
// A (after 1 second)
// B (after 1.5 seconds)
// C (immediately after B)
Explanation
observable1
emits'A'
after a 1-second delay.observable2
emits'B'
and'C'
after a 500ms delay following the completion ofobservable1
.
Example 3: Concatenating HTTP Requests
Fetch data sequentially from multiple APIs.
typescriptCopy codeimport { concat } from 'rxjs';
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
@Component({
selector: 'app-example',
template: `<div>Check the console for output</div>`,
})
export class ExampleComponent {
constructor(private http: HttpClient) {
const api1$ = this.http.get('https://api.example.com/data1');
const api2$ = this.http.get('https://api.example.com/data2');
concat(api1$, api2$).subscribe({
next: (data) => console.log('Received:', data),
complete: () => console.log('All requests complete'),
});
}
}
Explanation
api1$
andapi2$
are HTTP Observables.- The
concat()
operator ensuresapi2$
starts only afterapi1$
completes.
Error Handling with concat()
If any Observable in the sequence emits an error, the concatenation stops, and the error is propagated.
Example: Error Propagation
typescriptCopy codeimport { of, concat, throwError } from 'rxjs';
const observable1 = of('Success');
const observable2 = throwError('Error occurred');
const observable3 = of('Will not be emitted');
const result = concat(observable1, observable2, observable3);
result.subscribe({
next: (value) => console.log(value),
error: (err) => console.error('Error:', err),
complete: () => console.log('Complete'),
});
// Output:
// Success
// Error: Error occurred
Explanation
observable1
emits'Success'
and completes.observable2
emits an error, stopping the sequence.observable3
is never subscribed to.
Practical Use Cases
- Sequential HTTP Calls: Execute multiple API requests in sequence where the next request depends on the result of the previous one.
- Chaining Tasks: Run tasks or operations in a specific order.
- Combining Data Streams: Merge multiple streams of data into a single, sequential stream.
Advantages of Using concat()
- Predictable Order: Ensures the output maintains the same order as the input Observables.
- Streamline Sequential Logic: Simplifies managing tasks that need to execute in a specific sequence.
- Built-in Error Propagation: Stops execution and propagates errors automatically.
- Ease of Use: Works seamlessly with other RxJS operators for advanced use cases.
Limitations of concat()
- Sequential Execution: If one Observable is slow or long-running, subsequent Observables are delayed.
- Error Sensitivity: Errors in any Observable halt the entire sequence.
- Not Parallel: Observables are executed one at a time, so it’s not suitable for parallel execution.
Alternatives to concat()
merge()
: Combines Observables and emits values as they arrive (parallel execution).concatMap()
: Maps each value to an inner Observable and concatenates their outputs.forkJoin()
: Runs Observables in parallel and emits all their results together after completion.
Conclusion
The RxJS concat()
operator is a powerful tool for combining multiple Observables in a sequential and predictable manner. Whether you’re managing sequential HTTP requests, chaining tasks, or creating ordered data streams, concat()
provides a clean and declarative way to handle such scenarios.
By mastering concat()
, you can simplify your reactive code and build efficient, sequential workflows in RxJS-based applications.