DavidC
DavidC

Reputation: 147

How to make Observable.flatMap to wait to resolve

Playing with RxJS and React, I'm having problem of how to wait for data in Observable.fromPromise generated within map on another Observable.

I have async helper:

const dataStreamGenerator = (url = CLIENTS_DATA_URL) => (
  Rx.Observable.fromPromise(fetch(url))
  .flatMap(response => Rx.Observable.fromPromise(response.json()))
  .catch(err => Rx.Observable.of(new Error(err)))
);

Then I have actions.fetchClients$ which is Rx.Subject:

actions.fetchClients$.map((url = CLIENTS_DATA_URL) => {
    const ts = Date.now();
    console.log('FETCH CLIENTS with: ', url);
    return dataStreamGenerator(url);
  }).map(val => {
    console.log('GOT DATA IN REDUCER: ', val);
    const error = (val instanceof Error) ? val.message : undefined;
    const data = error ? undefined : val;
    actions.receivedClientsData$.next({ data, error, ts: Date.now() });
    return (state) => state;
  })

(Yes, trying to mimick Redux in RxJS).

Whan I test the dataStreamGenerator, it works ok (with ava) and delivers data:

test('dataStreamGenerator', t => {
  const ds$ = dataStreamGenerator(CLIENTS_DATA_URL);
  return ds$.do((data) => {
    t.is(data.length, 10);
  });
});

(AVA automatically subscribe to observable and consume it, so there is no need to subscribe).

But the actions.fetchClients$.map((url = CLI... second map (starting... console.log('GOT DATA IN REDUCER: ', val);... is still getting the Observable and not the data from dataStream$.

I tried all possible combinations of map and flatMap in fetchClients$ code but still no luck.

My test code is:

test.only('fetchClients$', t => {
  const initialState = {};
  actions.fetchClients$.next('http://jsonplaceholder.typicode.com/users');
  reducer$.subscribe(val => {
    console.log('TEST SUBSCRIBE VAL: ', val);
    t.pass();
  });
  actions.fetchClients$.next('http://jsonplaceholder.typicode.com/users');
});

I cant figure out how to wait to the Observable dataStreamGenerator(url); to emit data and not the Observable.

Thanks.

Upvotes: 1

Views: 1119

Answers (1)

paulpdaniels
paulpdaniels

Reputation: 18663

You need to flatten the results of what you returned from dataStreamGenerator.

actions.fetchClients$
  //This is now a flatMap
  .flatMap((url = CLIENTS_DATA_URL) => {
    const ts = Date.now();
    console.log('FETCH CLIENTS with: ', url);
    return dataStreamGenerator(url);
  });

The map operator delivers the value as-is down stream, whereas flatMap will flatten Observables, Arrays and Promises such that their values are what get propagated.

It works in the test case because you are directly subscribing to the Observable returned by dataStreamGenerator.

Upvotes: 2

Related Questions