4

What I'm trying to achieve is to run a series of observables conditionally.

return observable.map(response => response)
       .flatmap(response1 => observable1(response1))
       .flatmap(response2 => observable2(response2))
       .flatmap(response3 => observable3(response3))

I need to check the response1 and invoke the remaining observables if needed, else I need to return response1 and break the execution and so on.

I've gone through the following SO questions but they doesn't seem to answer my question

Conditionally choose observable in RxJS

RXJS if with observable as conditional

Handle Error in RxJs flatMap stream and continue processing

I'm new to rxjs, so forgive me if the question seems too lame.

Any help will be appreciated.

Thanks

Community
  • 1
  • 1
Joel Raju
  • 1,210
  • 2
  • 18
  • 21
  • What is the condition based on? Is it static, does it depend on `response`, does the second condition depend on `response2` etc? – Bergi Mar 09 '17 at 21:22
  • yes, the condition is static, the second condition depends on the `response2` – Joel Raju Mar 10 '17 at 06:25

4 Answers4

6

This is like calling multiple consecutive HTTP requests where results depend on each other. This is where you want to use concatMap() instead because it'll wait until the Observable returned from the callback function completes.

It's hard to tell what exactly you want to do from your description but if you need to stop propagating the results (and avoid calling unnecessary HTTP requests) have a look takeWhile() operator or simple filter() operators.

With filter():

return observable
    .concatMap(response => response)
    .filter(response1 => response1.whatever === true)
    .concatMap(response1 => observable1(response1))
    .filter(response2 => response2.whatever === true)
    .concatMap(response2 => observable2(response2))
    .filter(response3 => response3.whatever === true)

This simply won't propagate the item further if it fails the filter test. However the source observable can still emit values that'll go through the chain. In other words the filter operator doesn't complete the chain.

With takeWhile():

return observable
    .concatMap(response => response)
    .takeWhile(response1 => response1.whatever === true)
    .concatMap(response1 => observable1(response1))
    .takeWhile(response2 => response2.whatever === true)
    .concatMap(response2 => observable2(response2))
    .takeWhile(response3 => response3.whatever === true)

If any of the takeWhile() result into false it'll complete the chain and no other value can be emitted.

Graham
  • 7,431
  • 18
  • 59
  • 84
martin
  • 93,354
  • 25
  • 191
  • 226
  • What's the difference between `concatMap` and the `flatMap` that the OP is using? – Bergi Mar 09 '17 at 22:24
  • I want to stop the propagation to `observable2` if `response1` is a certain value and return `response1` or else run `observable2` and so on – Joel Raju Mar 10 '17 at 06:34
  • can you show an example how to stop propagation using `filter()` or `takewhile()` – Joel Raju Mar 10 '17 at 07:35
2

You could do something like:

observable.map(res => res.json())
  .flatMap(json => {
      if (json.something === 'something') {
          return this.http.get('/anotherCall')
              .map(res => 'second response: ' + res);
      } else {
          return Observable.from(['just return something else']);
      }
  });

It's only two calls but you get the point.

Patrik
  • 116
  • 1
  • 9
  • Note : the 'return something else' can be `Observable.empty()` if you want the streams to complete immediately there. You cannot just *not* return some observable-like thing, this will cause an runtime error. – Pac0 Mar 23 '18 at 15:01
1

I did not find easy way to bypass operators, if you decide to skip n last operators, you still have to pass last valid response through all operators:

 return observable
   .flatmap(r => {
        return r.value > 1 ? observable1(r) : Observable.of(r);
   })
   .flatmap(r => {
        return r.value > 2 ? observable2(r) : Observable.of(r);
   })
   .flatmap(r => {
        return r.value > 3 ? observable3(r) : Observable.of(r);
   })
kemsky
  • 14,727
  • 3
  • 32
  • 51
0

Alternatively, you can combine all the observables into one single observable and subscribe to that as below,

return response
         .concatMapTo(response1,(response2, response3) => 
             `${response2} ${response3}`
          );

Subscribe to it using

data = response.subscribe(val => console.log(val)); // contains all three responses value

Still you can access each one separately using the response2 and response3 named instance.

Subscribing for a single object using different observables: If you are looking to combine data and properties from different observables you can have a look at this answer

Community
  • 1
  • 1
Aravind
  • 40,391
  • 16
  • 91
  • 110
  • I don't want to combine the responses, I only need to run `observable2` if I want to. – Joel Raju Mar 10 '17 at 06:28
  • you can return separately then? why you are using **flatMap**? – Aravind Mar 10 '17 at 06:33
  • or what else will I use if I want to run `observable2` if the `response1` is a certain value or return `response1` if it is some other value without nesting – Joel Raju Mar 10 '17 at 06:39