koral
koral

Reputation: 2965

Angular 2 + rxjs - how return stream of objects fetched with several subsequent http requests

I have data classes

class Processor {
    frequency: number;
    error: booelan;
}

class Computer {
    name: string;
    processor: Processor[];
}

I faetch it from backend using json:

{
    "name": "Alfa Beta",
    "processors": [
        {
            "url": "ASD3-455R-FRTT-ASEW"
        },
        {
            "url": "AQW2-DFFFR-367K-MMKE"
        }
    ]
}

and single processor

{
    "url": "ASD3-455R-FRTT-ASEW",
    "frequency": 2200,
    working: true
}

I need to return stream of Computer as I'd like to ask for processors status each minute. For single returned instance of Computer I need to send 3 http requests with dependencies on each other. Of course I'll use service class for this. The only trouble for me is how to create this stream, ie. this.http.get(this.mainUrl)?

I've found Reactive programming, HTTP and Angular 2 and chapter Executing a request with the result of a previous one but it didn't help.

Upvotes: 2

Views: 1847

Answers (1)

martin
martin

Reputation: 96959

This looks like a job for concatMap operator (btw, I had a very similar question some time ago How to use exhaustMap in ReactiveX/rxjs 5 in TypeScript).

For example running several request in order:

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/concatMap';
import 'rxjs/add/operator/do';

var urls = [
  'https://httpbin.org/get?1',
  'https://httpbin.org/get?2',
  'https://httpbin.org/get?3',
  'https://httpbin.org/get?4',
  'https://httpbin.org/get?5',
];     

Observable.from(this.urls)
  .concatMap(url => http.get(url))
  .subscribe(response => console.log(response.url))
;

This prints to console:

https://httpbin.org/get?1
https://httpbin.org/get?2
https://httpbin.org/get?3
https://httpbin.org/get?4
https://httpbin.org/get?5

Operator concatMap() maps each value (each URL in our case) into an Observable and re-emits all its values until it's complete. Then it carries on with the next Observable returned from the callable. We know that http.get() returns an Observable that emits just one value with the Response object and then completes.

See plnkr demo: http://plnkr.co/edit/PJ7SpkNgBjZz2h4uvRpK?p=preview (in app.component.ts)

You said the request depend on each other (one request is dependent on the result of the previous request?) so maybe a better solution could be the following:

http.get('https://httpbin.org/get?1')
  .do(response => console.log('Process response', response.url))

  .concatMap(response => {
    console.log('Previous response', response.url, ' about to run /get?2')
    return http.get('https://httpbin.org/get?2')
  })
  .do(response => console.log('Process response', response.url))

  .concatMap(response => {
    console.log('Previous response', response.url, ' about to run /get?3')
    return http.get('https://httpbin.org/get?3')
  })

  .subscribe(response => console.log('Done', response.url))
;

Output in console:

Process response https://httpbin.org/get?1
Previous response https://httpbin.org/get?1  about to run /get?2
Process response https://httpbin.org/get?2
Previous response https://httpbin.org/get?2  about to run /get?3
Done https://httpbin.org/get?3

Again, we're using concatMap() to turn each value into an Observable but this time we're using it to only guarantee the correct order and to be able to access the result from the previous http.get() call. I also used do() operator only to separate logic that processes the previous response from logic that creates the next request for better readability (even though this could all go into concatMap()).

See plnkr demo: http://plnkr.co/edit/VFv09dTekK8av1Pu3a75?p=preview (in app.component.ts)

Upvotes: 3

Related Questions