rxjs:在 switchMap'ped 之后访问原始 observable 的结果

rxjs: access the result of original observable after it was switchMap'ped

如何在 tap 运算符 switchMapped 之后访问 resultB

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?
            })
        );

    })
);

奖金问题: 是否可以避免这里的嵌套,将整个流程链在单管道下?

请考虑以下示例:

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

这就是您如何编写可观察对象以访问 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
    })
);

这里的基本问题是 switchMap 用于转换值以将特定形状发射到流中。如果您的 resultB 值不是该形状的一部分,那么链中更下游的运算符将无法访问它,因为它们只接收发出的形状。

所以,基本上有两种选择:

  • 传递包含您的值的中间形状
  • 使用嵌套管道将两条数据放入同一运算符范围

到目前为止建议的解决方案涉及映射到中间对象。我的偏好是使用嵌套管道,因此流经流的数据是有意义的形状。但是,这真的取决于偏好。

使用嵌套管道,您的代码将如下所示:

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
            })
        ))
    ))
}