2

I want to apply an asynchronous transformation function on the value emitted by an observable.

@Injectable
export class ApiService{
    constructor(private http: HttpClient){}

    getSomething(url): Observable<any>{
        return this.http.get(url);
    }
}

In the code above, I want to apply a transformation function myFunc, which returns a promise, on the value emitted by this.http.get(url).

Normally I would use the map operator of RxJS, but since the transformation function returns a promise, I could not find a way to handle it.

For example, let my function be:

function myFunc(value){
    return new Promise((resolve, reject) => {
        // modify the value async

        resolve(modifiedValue);

        // ...
    });
}

Is there an appropriate way to handle this task? I think the following is not suitable, am I right?

return this.http.get(url).map(myFunc);

Any help will be much appreciated.

Note: I'm using RxJS 5.5.2

8
  • What makes you think it's unsuitable? Did you try it? What happened? Commented Dec 7, 2017 at 19:45
  • Yes, I tried. The value I receive was somewhat different than I expected. I got an object like {__zone_symbol__state: true, __zone_symbol__value : x} where is x is what I was expecting, but it's wrapped in another object as you see. I don't know what those zone symbols are though :) Commented Dec 7, 2017 at 19:58
  • Then it might help to show a minimal reproducible example of that, it's not clear what the problem is. Commented Dec 7, 2017 at 19:58
  • 1
    To clarify your problem I believe you are saying that your mapping function is async (returns a promise) so you can't use the map operator with it. Can you change the mapping function to return an observable and then use the switchMap operator instead of map? Why is your mapping function async btw? Commented Dec 7, 2017 at 19:59
  • 3
    OK, then maybe you can do: return this.http.get(url).switchMap(x => Observable.fromPromise(myFunc(x))); Commented Dec 7, 2017 at 20:03

1 Answer 1

3

Use the mergeMap operator to take in the response value, perform some modification asynchronously via another Observable operation, and then return the modified value. This operator will merge the values emitted by HttpClient and your modifier Observable and return a single Observable that emits the mutated values.

EDIT: Including the Observable.fromPromise bit from @bygrace's comment for a more complete answer.

i.e.

EDIT: For RxJs v5.5+

import { pipe } from 'rxjs/util/pipe';
import { mergeMap } from 'rxjs/operators';


    @Injectable
    export class ApiService{
        constructor(private http: HttpClient){}

        getSomething(url): Observable<any>{
            return this.http.get(url).pipe( 
                                        mergeMap(myFunc) 
                                      );
        }

        private myFunc(x): Observable<any> {
            // do some asynchronous modification that returns an Observable
            return Observable.fromPromise(x);
        }
    }

Pre RxJs v5.5

@Injectable
export class ApiService{
    constructor(private http: HttpClient){}

    getSomething(url): Observable<any>{
        return this.http.get(url)
               .mergeMap(data => {
                   // do some asynchronous modification that returns an Observable
                   return Observable.fromPromise(data);
                });
    }
}

See: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-mergeMap

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the answer. But I want to stick with observable instead of returning a promise. Because the consumers of the ApiService will call subscribe on the object returned by the getSomething method.
sorry, I misunderstood your goal. I see someone else suggested using switchMap which produces the same output as mergeMap. The only difference is they use a different operator internally under the hood.
@rawkfist0215 to clarify mergeMap and flatMap are the same. switchMap only maintains one inner subscription while mergeMap maintains multiple. So there is a subtle difference to be aware of. Essentially use switchMap if you want to cancel a request generated by the inner observable when the source emits.
@rawkfist0215 I want to try mergeMap as well. But I get the following tslint error: property 'mergeMap' does not exist on type 'Observable<any>' even though I imported mergeMap from 'rxjs/operators'. Have an idea?
@hswner You may want to try the pipe util operator if you are on a high enough version of RxJs and see if that clears up the tslint error. That's their new recommended best practice. See: github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.