rxjs: доступ к результату исходного наблюдаемого после того, как он был переключен на карту

Как мне получить доступ к resultB в операторе tap после того, как он был switchMapped ?

streamA$.pipe(
    switchMap(resultA => {
        const streamB$ = resultA ? streamB1$ : streamB2$;

        return streamB$.pipe(                   // <- nesting
            switchMap(resultB => loadData(resultB)),
            tap(data => {
                // how do I access resultB here?
            })
        );

    })
);

бонусный вопрос: можно ли здесь избежать вложенности и связать весь поток в одну трубу?


person eagor    schedule 14.09.2020    source источник
comment
Подсказка: можно получить доступ к resultA в tap, где вы хотите получить доступ к resultB. Почему? Ответ на этот вопрос должен сказать вам, что вам нужно сделать, чтобы получить доступ к resultB в файле tap.   -  person frido    schedule 14.09.2020


Ответы (3)


Пожалуйста, рассмотрите следующий пример:

streamA$.pipe(
  switchMap(resultA => resultA ? streamB1$ : streamB2$),
  switchMap(resultB => loadData(resultB).pipe(map(x => [resultB, x]))),
  tap([resultB, data] => {})
);
person Rafi Henig    schedule 14.09.2020

Вот как вы можете написать свою наблюдаемую, чтобы получить доступ к resultB и сгладить цепочку наблюдаемых операторов -

streamA$.pipe(        
    switchMap(resultA => iif(() => resultA ? streamB1$ : streamB2$),
    switchMap(resultB => forkJoin([loadData(resultB), of(resultB)])),
    tap(([loadDataResponse, resultB]) => {
        //loadDataResponse - This will have response of observable returned by loadData(resultB) method
        //resultB - This is resultB
    })
);
person user2216584    schedule 14.09.2020

Основная проблема здесь заключается в том, что switchMap используется для преобразования значений для передачи определенной формы в поток. Если ваше значение resultB не является частью этой формы, то операторы, расположенные дальше по цепочке, не будут иметь к ней доступа, потому что они получают только испускаемую форму.

Итак, есть в основном 2 варианта:

  • передать промежуточную форму, содержащую ваше значение
  • используйте вложенный канал, чтобы поместить обе части данных в одну и ту же область оператора

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

Используя вложенный канал, ваш код будет выглядеть примерно так:

streamA$.pipe(
    switchMap(resultA => {
        const streamB$ = resultA ? streamB1$ : streamB2$;

        return streamB$.pipe(
            switchMap(resultB => loadData(resultB).pipe(
                tap(data => {
                    // you can access resultB here
                })
            ))
        );
    })
);

Примечание: вы можете использовать iif для условного выбора исходного потока:

streamA$.pipe(
    switchMap(resultA => iif(()=>resultA, streamB1$, streamB2$).pipe(
        switchMap(resultB => loadData(resultB).pipe(
            tap(data => {
                // you can access resultB here
            })
        ))
    ))
);

Может быть полезно разбить часть логики на отдельные функции:

streamA$.pipe(
    switchMap(resultA => doSomeWork(resultA)),
    miscOperator1(...),
    miscOperator2(...)
);

doSomeWork(result) {
    return iif(()=>result, streamB1$, streamB2$).pipe(
        switchMap(resultB => loadData(resultB).pipe(
            tap(data => {
                // you can access resultB here
            })
        ))
    ))
}
person BizzyBob    schedule 15.09.2020