角度:有副作用,并从方法返回可观察



我有这个功能:

/**
* Attempt login with provided credentials
* @returns {Observable<number>} the status code of HTTP response
*/
login(username: string, password: string): Observable<number> {
let body = new URLSearchParams();
body.append('grant_type', 'password');
body.append('username', username);
body.append('password', password);
return this.http.post(Constants.LOGIN_URL, body, null)
.do(res => {
if (res.status == 200) {
this.authTokenService.setAuthToken(res.json().auth_token);
}
})
.map(res => res.status)
.catch(err => { return Observable.throw(err.status) });
}

此函数尝试执行登录并返回调用方可以使用的Observable<number>,以便收到响应的 HTTP 代码通知。

这似乎有效,但我有一个问题:如果调用方只是调用函数而不订阅返回的Observable,则不会调用do函数并且不会保存收到的身份验证令牌。

do州文件:

注意:这与可观察量的订阅不同。如果 可观察返回的 do 未订阅,副作用 观察者指定的永远不会发生。因此只是间谍 在现有执行中,它不会像 订阅确实如此。

我想实现的是,无论login()的调用者是否订阅,都有这种副作用。我该怎么办?

正如@Maximus所解释的,根据设计,冷可观察(如http call(在订阅它们之前不会发出任何数据。因此,您的.do()回调永远不会被调用。

另一方面,热可观察量将发出数据而不关心是否有订阅者。

您可以使用publish()运算符将冷Observable转换为ConnectableObservable。当调用其connect()方法时,该可观察对象将开始发出。

login(username: string, password: string): Observable < number > {
let body = new URLSearchParams();
body.append('grant_type', 'password');
body.append('username', username);
body.append('password', password);
let request = this.http.post(Constants.LOGIN_URL, body, null)
.do(res => {
if (res.status == 200) {
this.authTokenService.setAuthToken(res.json().auth_token);
}
})
.map(res => res.status)
.catch(err => {
return Observable.throw(err.status)
}).publish();
request.connect();
// type assertion because nobody needs to know it is a ConnectableObservable
return request as Observable < number > ; 
}

正如@Maximus所说。如果订阅发生在 ajax 调用完成后,则不会收到结果通知。要处理这种情况,您可以使用publishReplay(1)而不是简单的publish()PublishReplay(n)将向新订阅者重复源Observable发出的最后 n 个元素。

在下面的回答中,我假设您已经习惯了 AngularJS 中以前版本的HTTPPromises提供的行为:

login(username: string, password: string): Observable<number> {
return this.http.post(Constants.LOGIN_URL, body, null)
.then(res => {
if (res.status == 200) {
this.authTokenService.setAuthToken(res.json().auth_token);
}
})
.then(res => res.status)
.catch(err => { return Observable.throw(err.status) });

从调用返回this.http.get()可观察量cold可观察量。这意味着除非有人订阅它,否则它不会开始做任何事情。这是设计使然。链接到返回的可观察量的所有运算符都不会执行任何操作,因为没有订阅。

您需要订阅才能发出请求,然后与未来的订阅者共享结果。我认为AsyncSubject在这里是一个很好的候选人:

sent = false;
s = new AsyncSubject();
login(username: string, password: string): Observable<number> {   
if (!this.sent) {
this.http.post(Constants.LOGIN_URL, body, null)
.do(res => {
if (res.status == 200) {
this.authTokenService.setAuthToken(res.json().auth_token);
}
})
.map(res => res.status)
.catch(err => { return Observable.throw(err.status) })
.subscribe(s);
this.sent = true;
}
return s;
}

这样,将只进行一次 http 调用,并且包含的所有运算符do将只运行一次。之后,返回的结果将被缓存在 AsyncSubject 中,并将其传递给所有未来的订阅者。

最新更新