user1862925
user1862925

Reputation: 63

RxJS Observable.if executes both statements

So I am relatively new to RxJs and Observable patterns in general and am currently struggling to understand some of the features provided and why they behave the way they do.

Here is a code snippet :

Observable.if( //can also be onErrorResumeNext
  () => true,
  Observable.fromPromise(
    fetch('/'+ locale + '_state.json', {
      headers: {
        'Accept': 'application/json'
      },
      method: 'GET'
    }).then(res => {
      if (!res.ok) {
        throw new Error(res.statusText);
      }

      return res.json();
    })
  ),
  Observable.fromPromise(
    fetch('/'+ defaultLocale + '_state.json', {
      headers: {
        'Accept': 'application/json'
      },
      method: 'GET'
    }).then(res => {
      if (!res.ok) {
        throw new Error(res.statusText);
      }

      return res.json();
    })
  )
)

Why do both of these statements gets executed? Am I doing something/approaching this in the wrong way or is this expected behaviour? Returning something more simple like in the example of Observable.if works fine, but as far as I understand RxJs is meant to be mainly for asynchronous data so should not the example above behave the same way?

I am aware I can rewrite the behaviours of Observable.if and Observable.onErrorResumeNext using Observable.mergeMap and thats what I ended up doing but it feels like I am missing something out here.

Upvotes: 1

Views: 72

Answers (2)

artur grzesiak
artur grzesiak

Reputation: 20348

The thing is that promise is not lazy and executes immediately. To easiest way to make a computation lazy is to use .defer like that:

Observable.if( //can also be onErrorResumeNext
  () => true,
  Observable.defer(() =>
    fetch('/'+ locale + '_state.json', {
      headers: {
        'Accept': 'application/json'
      },
      method: 'GET'
    }).then(res => {
      if (!res.ok) {
        throw new Error(res.statusText);
      }

      return res.json();
    })
  ),
  Observable.defer(() =>
    fetch('/'+ defaultLocale + '_state.json', {
      headers: {
        'Accept': 'application/json'
      },
      method: 'GET'
    }).then(res => {
      if (!res.ok) {
        throw new Error(res.statusText);
      }

      return res.json();
    })
  )
)

.defer will automatically upgrade a promise to an observable.

Upvotes: 1

JLRishe
JLRishe

Reputation: 101730

You are calling fetch() twice and passing the result to Observable.if(). RxJs has no ability to control which one is called because you are already calling both before Observable.if() even runs.

The second and third parameters of your Observable.if() contain a lot of duplicate code. Why not rewrite this like so:

Observable.if(() => true, locale, defaultLocale)
.mergeMap(val => 
    fetch('/'+ val + '_state.json', {
        headers: {
            'Accept': 'application/json'
        },
        method: 'GET'
    }).then(res => {
        if (!res.ok) {
            throw new Error(res.statusText);
        }
             return res.json();
    })
);

Upvotes: 1

Related Questions