RXJS:返回外部 observable 的操作值

Posted

技术标签:

【中文标题】RXJS:返回外部 observable 的操作值【英文标题】:RXJS: Return the manipulated value of outer observable 【发布时间】:2018-09-12 22:52:13 【问题描述】:

我试图调用两个 observable,第二个的响应我操纵第一个,然后返回一个新的 observable。

return this.http.get(requestUrl)
            .map((response: User) => 
                sessionManager.currentUser = response;
                return this.isFirstTimeUser(response.id);
            )
            .do((response: any) => 
                console.log(response);
                sessionManager.currentUser.firstTimeLogin = response === 'true';
                return Observable.create(sessionManager.currentUser);
            )
            .toPromise();

isFirstTimeUser(response.id) 是另一个返回 observable 的函数。

private isFirstTimeUser(userId: number): Observable<any> 
        const requestUrl = 'someUrl';
        return this.http.get(requestUrl, responseType: 'text');
    

我想要做的就是从第一个 http 调用中获取用户对象,在第二个调用中使用用户的 id 发出另一个请求,然后在我从第二个 observable 得到响应后,我更新了一个属性第一个响应并返回更新后的第一个响应。我如何实现这一目标?我应该看什么 rxjs 函数?

【问题讨论】:

在你的例子中,你在哪里调用第二个 observable? isFirstTimeUser(response.id) 返回另一个 http Observable。 查看我的更新答案 你能更好地解释你想要做什么吗?我试图想出答案,但你的描述不清楚。 @codeepic 编辑有帮助吗? 【参考方案1】:

您正在寻找的解决方案是这样的:

getUser(): Observable<any>
    return this.http.get(reqUrl)
        .do((res: User) => 
            sessionManager.currentUser = res;
        )
        .switchMap((res: User) => 
            return this.isFirstTimeUser(res.id);
        )
        .do((res: any) => 
            console.log(response);
        
        .map((res: any) => 
            sessionManager.currentUser.firstTimeLogin = res === 'true';
            return sessionManager.currentUser;
        );

那么你只需要订阅getUser() 就可以了。根据纯函数的 rxjs 函数范式,它仍然不是最好的代码。我想使用带有投影功能的concatMap 会让我们更接近理想,但它会起作用。

它不理想的原因是因为在第一个 .do() 块中有一个必需的副作用,我们更新了 sessionManager 值 - 函数外部的变量(函数世界中的一个大禁忌)然后我们访问它再次在.map() 函数中。 concatMap 将投影函数作为第二个参数将帮助我们解决这个问题。

【讨论】:

【参考方案2】:

你的例子很接近。

您想使用switchMap,它取消订阅传入的可观察对象,并订阅下一个可观察对象。

return this.http.get(requestUrl)
        .switchMap((response: User) => 
            sessionManager.currentUser = response;
            return this.isFirstTimeUser(response.id);
        )
        .map((response: any) => 
            console.log(response);
            sessionManager.currentUser.firstTimeLogin = response === 'true';
            return sessionManager.currentUser;
        )
        .toPromise();

响应将被传递给isFirstTimeuser(),随后.map() 将应用于该函数返回的可观察对象。

之所以称为switchMap,是因为this.http.get() 返回的可观察对象已取消订阅,并且使用了下一个可观察对象。这不会是this.http.get() 的问题,但请记住switchMap 仅适用于第一个 发出的值。

【讨论】:

这太美了!非常感谢!【参考方案3】:

使用mergeMap

return this.http.get(requestUrl)
            .mergeMap((response: User) => 
                const currentUser = response;
                currentUser.firstTimeLogin = this.isFirstTimeUser(response.id) === 'true' 
                return Observable.of(currentUser);
            )

【讨论】:

你的例子我不清楚,所以我用了这个 这不是我的例子。这是我的问题。我正在为我的特定问题寻找解决方案,因为我不太精通 rxjs。你认为你可以编辑答案来解决我的问题吗? 在 rxjs 中你不应该修改除了可观察的发射之外的任何变量,在这里你改变 sessionManager,这在 RX 的上下文中是无效的 这是真的,如果你这样做sessionManager.currentUser = response 这是一个不好的做法。因为它是可观察流之外的副作用。 this.isFirstTimeUser(response.id) === 'true' 永远不会是真的。它返回一个可观察的。请参阅 OPs cmets。

以上是关于RXJS:返回外部 observable 的操作值的主要内容,如果未能解决你的问题,请参考以下文章

Angular之Rxjs基础操作

RxJS之组合操作符 ( Angular环境 )

rxjs Observable 两大类操作符简介

如何使用 catchError() 并且仍然返回带有 rxJs 6.0 的类型化 Observable?

RxJS之转化操作符 ( Angular环境 )

RxJS之过滤操作符 ( Angular环境 )