Angular:有副作用并从方法返回Observable

时间:2017-08-09 09:45:23

标签: angular typescript rxjs

我有这个功能:

  /**
   * 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州的文档:

  

注意:这与Observable上的订阅不同。如果   do返回的observable没有订阅,副作用   观察者指定的情况永远不会发生。因此,只是间谍   在现有执行中,它不会触发执行就好   订阅确实。

我想要实现的是,无论login()的来电者是否订阅,都会产生这种副作用。我该怎么办?

2 个答案:

答案 0 :(得分:2)

正如@Maximus所解释的那样,在设计之前,冷Observable(如http调用)在订阅它们之前不会发出任何数据。所以你的.do()回调永远不会被调用。

另一方面,如果有订户,热门观察者将无需照顾就会发出数据。

您可以使用Observable运算符将您的冷ConnectableObservable转换为publish()。 Observable将在调用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个元素。

答案 1 :(得分:1)

在下面的回答中,我假设您习惯了Private Sub textBox5_KeyPress(sender As System.Object, e As System.Windows.Forms.KeyPressEventArgs) Handles textBox5.KeyPress If Asc(e.KeyChar) <> 8 Then If Asc(e.KeyChar) < 48 Or Asc(e.KeyChar) > 57 Then e.Handled = True End If End If End Sub 在AngularJS中以前版本的Promises中提供的行为:

HTTP

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) }); 调用返回的observable是this.http.get()可观察的。这意味着除非有人订阅,否则它不会开始做任何事情。那是设计上的。您链接到返回的观察者的所有操作员都不会做任何事情,因为没有订阅。

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

AsyncSubject

这样只会进行一次http调用,所有包含 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; } 的运算符只会运行一次。之后,返回的结果将缓存在do中,并将其传递给所有未来的订阅者。