Converting RxJS Observables to Async/Await

Published: (December 6, 2025 at 11:16 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Comparing Reactive and Asynchronous Streams: Equivalent Transformations

Asynchronous Programming: The Basics

Asynchronous programming allows a program to start an operation without waiting for it to complete immediately. The program continues to execute other tasks and is later notified when the asynchronous operation finishes. This is usually achieved through callbacks, Promises, or async/await. It is excellent for handling I/O operations such as network requests or file reading, where the wait time can be significant.

Reactive Streams: Reacting to Data

Reactive streams take a data‑oriented approach, focusing on how data is emitted, received, and transformed over time. A reactive stream is a sequence of events that can be observed and reacted to. Frameworks like RxJava, RxJS, and Reactor provide tools to create, combine, and transform data streams declaratively. Their main advantage is handling continuously arriving data in varying volumes, making them ideal for real‑time systems and streaming data processing.

Comparing the Approaches

FeatureAsynchronous ProgrammingReactive Streams
FocusNon‑blocking execution of operations.Data streams and reactivity.
Data ModelGenerally works with single data or results.Operates on streams (sequences) of data.
NotificationCallbacks, Promises, async/await.Observers and Subscriptions.
ComplexityLess complex for simple operations.Can be more complex to understand at first.
Use CasesI/O, background tasks.Real‑time systems, streaming data.
Main AdvantageImproved responsiveness.Flexibility in handling data streams.

Equivalent Transformations: Asynchronous vs. Reactive

Single Asynchronous Call (Promise) and Reactive Stream

Asynchronous (JavaScript with Promises):

async function fetchData() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  console.log(data);
}
fetchData();

Reactive (RxJS):

import { fromFetch } from 'rxjs/fetch';
import { switchMap, of, tap } from 'rxjs';

fromFetch('https://api.example.com/data')
  .pipe(
    switchMap(response => {
      if (response.ok) {
        return response.json();
      } else {
        return of({ error: response.status });
      }
    }),
    tap(data => console.log(data))
  )
  .subscribe();

In this example, the asynchronous fetch call is equivalent to a reactive stream that emits a single value after the request completes.

Mapping (Map) and Data Processing

Asynchronous (JavaScript with Promise.all):

async function processData() {
  const results = await Promise.all([
    fetch('https://api.example.com/data1').then(res => res.json()),
    fetch('https://api.example.com/data2').then(res => res.json())
  ]);
  const mappedResults = results.map(item => item.value * 2);
  console.log(mappedResults);
}
processData();

Reactive (RxJS):

import { forkJoin, from } from 'rxjs';
import { switchMap, map } from 'rxjs/operators';
import { fromFetch } from 'rxjs/fetch';

forkJoin({
  data1: fromFetch('https://api.example.com/data1').pipe(switchMap(res => res.json())),
  data2: fromFetch('https://api.example.com/data2').pipe(switchMap(res => res.json()))
})
  .pipe(
    map(results => [results.data1.value * 2, results.data2.value * 2])
  )
  .subscribe(mappedResults => console.log(mappedResults));

Both examples demonstrate how to transform received data. In the asynchronous approach, Promise.all and Array.map are used; in the reactive approach, forkJoin (to combine streams) and the RxJS map operator are used.

Conclusion

Both asynchronous programming and reactive streams are powerful tools for building modern applications. The choice between them depends on the specific requirements of the project. Asynchronous programming is suitable for simple tasks where responsiveness is crucial, while reactive streams excel in scenarios involving continuous data streams and complex transformations. Understanding the equivalencies between these approaches lets you leverage their strengths and create robust, efficient applications. Combining the two techniques is often the key to success in complex systems.

Back to Blog

Related posts

Read more »

Code Block Click Copy

I'm a back‑end‑leaning full‑stack developer, and after a long day fixing JavaScript code I decided to add “copy to clipboard” buttons to the code blocks on my s...