RxJS и Angular HttpClient: как асинхронно преобразовать значение?

Я хочу применить функцию асинхронного преобразования к значению, испускаемому наблюдаемым.

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

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

В приведенном выше коде я хочу применить функцию преобразования myFunc, которая возвращает обещание, к значению, сгенерированному this.http.get(url).

Обычно я бы использовал оператор карты RxJS, но, поскольку функция преобразования возвращает обещание, я не смог найти способ с этим справиться.

Например, пусть моя функция будет:

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

        resolve(modifiedValue);

        // ...
    });
}

Есть ли подходящий способ справиться с этой задачей? Я думаю, что следующее не подходит, я прав?

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

Любая помощь будет высоко ценится.

Примечание. Я использую RxJS 5.5.2.


person hswner    schedule 07.12.2017    source источник
comment
Что заставляет вас думать, что это не подходит? Вы пробовали? Что случилось?   -  person jonrsharpe    schedule 07.12.2017
comment
Да, я пытался. Значение, которое я получаю, несколько отличается от того, что я ожидал. Я получил такой объект, как {__zone_symbol__state: true, __zone_symbol__value : x}, где x — это то, что я ожидал, но, как вы видите, он завернут в другой объект. Я не знаю, что это за символы зон :)   -  person hswner    schedule 07.12.2017
comment
Тогда может помочь показать минимально воспроизводимый пример этого, неясно, что проблема в.   -  person jonrsharpe    schedule 07.12.2017
comment
Чтобы прояснить вашу проблему, я полагаю, вы говорите, что ваша функция отображения является асинхронной (возвращает обещание), поэтому вы не можете использовать с ней оператор map. Можете ли вы изменить функцию сопоставления, чтобы она возвращала наблюдаемую, а затем использовать оператор switchMap вместо map? Кстати, почему ваша функция сопоставления асинхронна?   -  person bygrace    schedule 07.12.2017
comment
@bygrace myFunc — это метод из сторонней библиотеки. Я проверю switchMap. Спасибо.   -  person hswner    schedule 07.12.2017
comment
@bygrace Было бы супер, если бы вы могли привести простой пример.   -  person hswner    schedule 07.12.2017
comment
Хорошо, тогда, может быть, вы можете сделать: return this.http.get(url).switchMap(x => Observable.fromPromise(myFunc(x)));   -  person bygrace    schedule 07.12.2017
comment
Прохладно. Это сработало. Я знал, что это так просто, но все еще не мог собрать все воедино в своей голове. Я очень новичок в RX. Спасибо еще раз! @по благодати   -  person hswner    schedule 07.12.2017


Ответы (1)


Используйте оператор mergeMap, чтобы получить значение ответа, выполнить некоторые изменения асинхронно с помощью другой операции Observable, а затем вернуть измененное значение. Этот оператор объединит значения, выдаваемые HttpClient и вашим модификатором Observable, и вернет один Observable, который выдает измененные значения.

РЕДАКТИРОВАТЬ: включая бит Observable.fromPromise из комментария @bygrace для более полного ответа.

i.e.

EDIT: для 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);
        }
    }

Предварительно 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);
                });
    }
}

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

person rawkfist0215    schedule 07.12.2017
comment
Спасибо за ответ. Но я хочу придерживаться наблюдаемого вместо того, чтобы возвращать обещание. Поскольку потребители ApiService будут вызывать подписку на объект, возвращаемый методом getSomething. - person hswner; 07.12.2017
comment
извините, я неправильно понял вашу цель. Я вижу, что кто-то еще предложил использовать switchMap, который дает тот же результат, что и mergeMap. Единственная разница в том, что внутри они используют другого оператора. - person rawkfist0215; 07.12.2017
comment
@rawkfist0215, чтобы уточнить, что mergeMap и flatMap одинаковы. switchMap поддерживает только одну внутреннюю подписку, а mergeMap поддерживает несколько. Так что есть тонкая разница, о которой нужно знать. По сути, используйте switchMap, если вы хотите отменить запрос, сгенерированный внутренним наблюдаемым, когда источник испускает. - person bygrace; 07.12.2017
comment
@rawkfist0215 Я тоже хочу попробовать mergeMap. Но я получаю следующую ошибку tslint: property 'mergeMap' does not exist on type 'Observable<any>', хотя я импортировал mergeMap из «rxjs/operators». Есть идея? - person hswner; 07.12.2017
comment
@hswner Вы можете попробовать оператор pipe util, если у вас достаточно высокая версия RxJs, и посмотреть, устранит ли это ошибку tslint. Это их новая рекомендуемая передовая практика. См.: github.com/ReactiveX/rxjs/blob/master/ doc/lettable-operators.md - person rawkfist0215; 07.12.2017
comment
@rawkfist0215 теперь все в порядке. Я вижу, что труба - это путь;) - person hswner; 07.12.2017