Rxjs: в зависимости от наблюдаемого значения, передайте с помощью mergeMap или switchMap

#rxjs #rxjs6

#rxjs #rxjs6

Вопрос:

У нас есть служба запросов, которая выполняет http-запросы. Все запросы реализуют интерфейс IRequest, который содержит http-метод и функцию execute, которая, в свою очередь, возвращает другое наблюдаемое значение.

RequestService имеет перегрузку, которая принимает исходное наблюдаемое и requestfactory, который возвращает экземпляр IRequest на основе значения исходного наблюдаемого.

 export interface IRequest<TResponse> {
    __method: string;
    execute(): Observable<TResponse>;
}

class RequestService<TSource, TResponse> {
    public execute(source: Observable<TSource>, requestFactory: (input: TSource) => IRequest<TResponse>): Observable<TResponse> {
        return source.pipe(
            map(x => requestFactory(x)),
            switchMap(x => x.execute()),
        );
    }
}
  

Конечно, в службе выполняется намного больше (ведение журнала, обработка ошибок по умолчанию, …), но большая часть не имеет отношения к вопросу.

Теперь все это работает как по волшебству, но то, что мы хотели бы сделать, это использовать switchMap для GET-запросов и ‘mergeMap’ для запросов POST, PUT, DELETE по умолчанию.

Похоже, мы не можем заставить это работать. Нам нужен доступ к экземпляру IRequest после того, как он был создан фабрикой, что возможно только в том случае, если у нас есть исходное наблюдаемое значение. Однако при использовании оператора мы больше не можем контролировать выбор между switchMap или mergeMap.

Нашим следующим выбором было создать наш собственный, RequestMapOperator который создавал бы экземпляр MergeMapOperator or SwitchMapOperator и использовал соответствующие методы next, complete и error. Однако SwitchMapOperator не экспортируется библиотекой rxjs.

Итак, прежде чем создавать наш собственный класс Subscriber, который был бы просто комбинацией MergeMapSubscriber и SwitchMapSubscriber , мы хотели бы знать, не упускаем ли мы чего-то.


TL; DR

Нам нужен оператор, который, в зависимости от значения исходной наблюдаемой, преобразуется в новую наблюдаемую с помощью mergeMap или switchMap.

Комментарии:

1. С этой проблемой я столкнулся. Мы прибегли к написанию нашего собственного, RequestMapOperator но столкнулись с некоторыми проблемами (переключение между различными реализациями _next и условиями гонки) Если вы нашли решение, пожалуйста, поделитесь им! Спасибо!

2. Все еще надеясь, что кто-нибудь придумает ответ 🙂 На данный момент мы тоже прибегли к написанию нашего собственного RequestMapOperator и Subscriber путем сравнения и объединения источника из операторов mergeMap и switchMap. В нашем случае мы предполагаем, что для одного и того же наблюдаемого источника (запроса) выбор между переключением и функциональностью слияния никогда не меняется, потому что метод запроса никогда не меняется. Если это произойдет, мы выдадим ошибку (в _next методе, подобном тому, который вы упомянули), чего на самом деле, вероятно, никогда не будет.

Ответ №1:

Вы могли бы использовать функцию RXJS iif, которая принимает функцию условия в качестве первого параметра и на основе возвращаемого значения решает, на какую наблюдаемую подписаться.

Например.

 class RequestService<TSource, TResponse> {
    public execute(source: Observable<TSource>, requestFactory: (input: TSource) => IRequest<TResponse>): Observable<TResponse> {
        return source.pipe(
            map(x => requestFactory(x)),
            // using mergemap outside because we don't want to cancel inner 
            // observable
            margeMap((x) => 
              iif(() => x.method === "GET", 
               switchMap(x => x.execute()),
               margeMap(x => x.execute())
              )
            )
        );
    }
}
  

Надеюсь, это поможет.

Комментарии:

1. Это именно то, что я попробовал сначала, но iif оператор ожидает, что observable (ну, subscribable, чтобы быть правильным) в качестве второго и третьего параметров. Оба switchMap и mergeMap имеют тип OperatorFunction . Хотя спасибо за предложение.