0

I have Vanilla.js (TypeScript) HTTP client that returned Promises, like so:

class Person {
    name: string;
    id: number;
    friendsIds: number[];
}

class MyClient {

    getPersonInfo(personId: number): Promise<Person> {

        return fetch( '/people/' + personId)
            .then( r => r.json() as Person );
    }
}

My display code gets the details of each Person's friends, like so:

refresh(): void {

    client.getPersonInfo( 123 )
        .then( (person: Person) => {

            // array-of-promises:
            let friendsDetailsPromises: Promise<Person>[] = person.friendsIds.map( friendId => client.getPersonInfo( friendId  ) );

            // promise-of-array:
            let friendsDetailsAll     : Promise<Person[]> = Promise.all( friendsDetailsPromises );

            return friendsDetailsAll;
        } )
        .then( (friendsDetails: Person[]) => {

            // (do stuff for each Person)
        } )
        .catch( reason => console.error( reason ) );
}

I'm trying to move this over to Angular 2's built-in HTTP client which returns Observable<T> instead of Promise<T>, so far I have:

class MyClient {

    private http: HttpClient; 

    getPersonInfo(personId: number): Observable<Person> {

        return this.http.get<Person>( '/person/' + personId );
    }
}

refresh(): void {

    client.getPersonInfo( 123 )
        .flatMap( (person: Person) => {

            // ...now what?
        } )
        .subscribe(
            (friendsDetails: Person[]) => {

            // (do stuff for each Person)
            },
            err => console.error( err )
        )
    );
}

I'm stuck on what I'm supposed to do inside of flatMap.

I saw this QA which asks about converting Promise.all to Observable ( Promise.all behavior with RxJS Observables? ) and the answer is to use forkJoin but also suggests flatMap and linking to this posting ( RxJS Promise Composition (passing data) ) but this problem doesn't resemble mine at all.

Dai
  • 141,631
  • 28
  • 261
  • 374
  • try `return Observable.combineLatest(person.friendsIds.map( friendId => client.getPersonInfo( friendId ) )` – Harry Ninh Sep 14 '17 at 03:13
  • @HarryNinh How would that change if I move to use `Subject` instead of creating a new `Observable` for each request? (I assume Angular's HTTP client creates a new `Observable` for each request - but doesn't that defeat the point of it being "observable" in the first place, because it won't update its state?) – Dai Sep 14 '17 at 03:18
  • Sorry not crystal clear about your approach of how you're gonna use `Subject` here. For me `Observable` and `Promise` are very much serving the same purpose here in `Http`, if you were okay with having an array of `Promise`, what stops you from having an array of `Observable`? – Harry Ninh Sep 14 '17 at 03:28
  • @HarryNinh I didn't return a `Promise[]` - I used `Promise.all` to convert it to `Promise` and I returned that. I'm asking what the Observable equivalent of `Promise.all` is - and if there's a better, RxJS-idiomatic, approach. – Dai Sep 14 '17 at 03:30
  • Yes you didn't, but in the process you have already created `friendsDetailsPromises: Promise[]`, which is an array of promises. Same here, `Observable.combineLatest` is used to combine an array of `Observable` to a single `Observable`. – Harry Ninh Sep 14 '17 at 03:41

1 Answers1

2

You can use Observable.forkJoin which will wait for all sub observables to complete(same as Promise.all).

I find the below syntax which works(Maybe not the best and wait for suggestions)

this.http.get('list.json').switchMap(res => {                   
  return Observable.forkJoin(res.map(item => this.http.get(item + '.json')));  
})

Refer this Plunker demo.


Sorry, Maybe this is a little late for it took some time to find new usage about rxjs5.

Pengyy
  • 37,383
  • 15
  • 83
  • 73