user3174602
user3174602

Reputation: 48

RXJS do operator not getting hit in stream when subscribe is called without a callback parameter

I am experimenting with a Redux-like pattern to maintain state in a service for a particular feature. My intent is that a consumer can subscribe to separate streams of data by calling getItem1() or getItem2(), then call action methods such as initializeItem1() or initializeItem2() on the service as desired to dispatch actions that will change the state of the store. In the service, data may be obtained either from an http call or something else as is simulated in the provided plnkr.

https://plnkr.co/edit/f3HLp5C8ckIpjNsM8dTm?p=preview

Specifically, what I do not understand is why, in my sample code, the observable returned by getItem1() does not emit a value unless I change this.service.initializeItem1().subscribe(); to this.service.initializeItem1().subscribe(() => {});. Can someone please explain if this seems like a bug in rxjs, if there is a good / known explanation as to why this behaves this way, or if I am just missing something? I appreciate any insight the community can provide me with. Thanks.

Component

//our root app component
import {Component, OnInit} from '@angular/core'
import {Observable} from 'rxjs/Rx'
import {SampleService} from './sample.service'

@Component({
  selector: 'my-app',
  template: `
    <div>
      <h2>Item 1 Value</h2>
      <div>{{ item1 }}</div>

      <h2>Item 2 Value</h2>
      <div>{{ item2 }}</div>
    </div>
  `,
})
export class App implements OnInit {
  name:string;
  item1: string;
  item2: string;
  constructor(private service: SampleService) { }

  ngOnInit() {
    this.item1 = this.service.getItem1().subscribe(item1 => this.item1 = item1);
    this.item2 = this.service.getItem2().subscribe(item2 => this.item2 = item2);

    /* broken */
    //Item 1 does not display
    //Item 2 displays as expected
    this.scenario1();

    /* works */
    //Item 1 displays as expected
    //Item 2 displays as expected
    // this.scenario2();
  }

  private scenario1() {
    this.service.initializeItem1().subscribe();
    this.service.initializeItem2().subscribe();
  }

  private scenario2() {
    this.service.initializeItem1().subscribe(() => {});
    this.service.initializeItem2().subscribe();
  }
}

Service

import {Injectable} from '@angular/core'
import {Observable, Subject} from 'rxjs/Rx'
import { Http, Headers } from '@angular/http';

@Injectable()
export class SampleService {
  constructor(private http: Http) { }
  private headers: Headers = new Headers({ 'Content-Type': 'application/json' });
  private _store: Subject<any> = new Subject<any>();
  private store = this._store.asObservable()
      .startWith({})
      .scan(reducer);

  public initializeItem1(): Observable<string> {
    return this.http.get('api/foobar')
      .map(response => response.json().item).do(data => {
        console.log('not hit in scenario 1, but hit in scenario 2:', data);

        this._store.next({
          type: 'INITIALIZE_1',
          payload: data
        });
      });
  }

  public initializeItem2(): Observable<string> {
    return Observable.of('foobar-2').do(data => {
      console.log('hit in scenario 1 and scenario 2:', data);

      this._store.next({
        type: 'INITIALIZE_2',
        payload: data
      });
    });
  }

  getItem1(): Observable<string> {
        return this.store.map(store => store.settings1);
    }

  getItem2(): Observable<string> {
      return this.store.map(store => store.settings2);
  }
}

function reducer(store, action) {
    switch(action.type) {
        case 'INITIALIZE_1':
            return Object.assign({ }, store, {
                initialSettings: action.payload,
                settings1: action.payload
            });
        case 'INITIALIZE_2':
            return Object.assign({ }, store, { settings2:action.payload });
        default:
            return store;
    }
}

Upvotes: 3

Views: 404

Answers (1)

martin
martin

Reputation: 96899

This is a bug in RxJS 5 up to the RxJS 5.2.0 version. It's fixed already but it hasn't been released yet.

The only workaround is to use any subscriber for example () => {} (I think using just {} should work as well).

Merged PR on RxJS GitHub fixing this issue:

Related Issues:

Upvotes: 3

Related Questions