Observables, Side-effects, and Subscriptions
Tags: reactivex, observable, reactiveprogramming, functional
Published: 2018-12-25 23:16:17

---
title: Observables, Side-effects, and Subscriptions
published: true
description: Exploring examples in idiomatic uses of Observables
tags: reactivex, observable, reactive programming, fp
canonical_url: https://blog.eyas.sh/2018/12/observables-side-effects-and-subscriptions/
---

My previous articles on [using `AsyncPipe`](https://blog.eyas.sh/2018/12/use-asyncpipe-when-possible/) and [data refresh patterns in Angular](https://blog.eyas.sh/2018/12/data-and-page-content-refresh-patterns-in-angular/) hint at some common anti-patterns dealing with Observables. If there’s any common thread in my advice, it is: **delay unpacking an Observable into its scalar types** when performing logic you can rewrite as side-effect-free, leaving code with side-effects for subscription callbacks and other downstream logic.

My two earlier articles focused on cases users can benefit from handling more of the object's lifecycle in its Observable form. In other words, cases where the Observable was being subscribed to and unpacked *too soon*. Instead, I suggested transforming the Observable using operators like `map`, `switchMap`, `filter`, etc. and taking advantage of the power offered by this form. In the case of Angular, it provides [`AsyncPipe`](https://angular.io/api/common/AsyncPipe), which takes the care of the step with side-effects (actually rendering the page) in template code.

There are some exceptions to this line of thinking, namely [`do` and `tap`](http://reactivex.io/documentation/operators/do.html) are reactive operators exclusively there *for* functions with side effects. I'll leave a discussion of right vs less right reasons to use `do`/`tap` for a later article. But I'll mention logging, error reporting, and caching of otherwise pure functions as one valid use of side-effects.

Let's explore a few cases:

# 1. Displaying data represented by Observables

Say I have two Observables wrapping some object in a storage format (e.g. JSON), and I'd like to display it.

## Unpacking an observable *too soon*

```ts
let customerName: string;
let customerBalance: number;

nameObservable.subscribe(name => {
  customerName = name;
  if (customerName && customerBalance) {
    processAndDraw();
  }
});
balanceObservable.subscribe(balance => {
  customerBalancer = balance;
  if (customerName && customerBalance) {
    processAndDraw();
  }
});
function processAndDraw() {
  alert(`${customerName}: $${customerBalance.toFixed(2) USD`);
}

```

If a caller unpacks an observable too soon, it means they're dealing with scalars, passing things around by global state. Developers might have trouble handling changes, such as adding a third data source to show.

## Unpacking an Observable *too late*

```ts
combineLatest(nameObservable, processAndDraw).pipe(
  map(([name, balance]) => {
    alert(`${name}: $${balance.toFixed(2) USD`);
  })
).subscribe();
```

On the one hand, this is much shorter and more expressive! This is effectively maps `Observable<[string, number]>` into an `Observable<void>` which *happens to perform side effects when subscribed to*. The subscriber, however, has no idea what action will take place from just looking at a type or signature. Even with the code snippet above used as-is, it is very easy to forget about that last `.subscribe()` call, which--given that Observables are lazy by default and only perform useful actions when subscribed to--renders this whole snippet a no-op.

One final reason side-effects are bad in operators: that these side-effects can be performed an arbitrary number of times per event based on how many distinct subscribers are listening to an Observable.

## A better trade-off

```ts
combineLatest(nameObservable, processAndDraw).pipe(
  map(([name, balance]) =>
    `${name}: $${balance.toFixed(2) USD`
  )
).subscribe(text => alert('Text'));
```
# Other use cases described in the [full piece](https://blog.eyas.sh/2018/12/observables-side-effects-and-subscriptions/)

2. Avoiding Unnecessary Indirection through `Subject`s
3. Subscribing when `switchMap` or `flatMap` would do

# Summary

An Observable going through a series of transformation operators from source to final result is:

1. Cancelable through-and-through; cancelling a subscription to a resultant Observable will cancel any underlying subscriptions opened to that end.
2. Composable in its own right; and
3. A ubiquitous immutable API that gives callers flexibility in manipulating return values.

I propose side-effects being a great first-order heuristic as far as what can reasonably be kept within a composed Observable. When needed, operators like do and tap will sometimes make sense.

[Read the full original article at blog.eyas.sh](https://blog.eyas.sh/2018/12/observables-side-effects-and-subscriptions/).