codamit.dev

Eager vs Lazy in RxJS

January 21, 2020

I made a mistake many times in RxJS when I use a function to perform a side effect in a stream.

The following factory function creates a Book object.

function createBook({ title, description }): Observable<Book> {
  return of({ title, description });
}

I want to use this function in a pipeline, let’s imagine a simple HTTP application.

fromRoute('/book', 'POST').pipe(
  switchMap(({ title, description }) => createBook({ title, description })),
  map((book) => ({ status: 204, body: book }))
);

Here the book is created only when /book endpoint is hit by a user. That’s because we’re just returning an observable with no side effects.

Now, imagine the following scenario where we need to perform a side effect operation using a database. Let’s assume that DB.create returns a Promise.

class BookDAO {
  static create({ title, description }): Observable<Book> {
    return from(
      DB.create({ title, description }) // 👈 Book created eagerly
    );
  }
}

fromRoute('/book', 'POST').pipe(
  switchMap(({ title, description }) =>
    BookDAO.create({ title, description }) // ❌ Book created at route evaluation
  ),
  map(book => ({ status: 204, body: book }))
);

Here the book is created in the database as soon as we bootstrap the server, before the source emits any values. That’s because the DB.create method returns a Promise which is eagerly executed when using the from factory.

To fix this issue we need to defer the operation, for creating the observable only when the source is subscribed.

class BookDAO {
  static create({ title, description }): Observable<Book> {
    return defer(() => DB.create({ title, description })); // 👈 Book creation is deferred
  }
}

fromRoute('/book', 'POST').pipe(
  switchMap(({ title, description }) =>
    BookDAO.create({ title, description }) // ✔️ Book created lazily
  ),
  map(book => ({ status: 204, body: book }))
);

Now the create method is lazy, which means that the resource is created only when the /book endpoint is hit by a user.

I made this mistake many times, especially when a function is used to produce a stream that perform a side effect.

I'm Edouard Bozon, I play almost everyday with web technologies. I focus my work on building better JavaScript apps and contributing to open source. I'm freelancer, currently working on awesome projects.