Angular 2 observable 订阅两次执行调用两次

Angular 2 observable subscribing twice executes call twice

问题
我订阅了一个 httpClient.get observable 两次。但是,这意味着我的调用被执行了两次。这是为什么?

证明
对于我执行的每个 subscribe(),我都会在登录页面中得到另一行。

代码(来自登录页面按钮的 onSubmit())

var httpHeaders = new HttpHeaders()
  .append("Authorization", 'Basic ' + btoa(this.username + ':' + this.password));

var observable = this.httpClient.get('api/version/secured', { headers: httpHeaders});
observable.subscribe(
  () => {
    console.log('First request completed');
  },
  (error: HttpErrorResponse) => {
    console.log('First request error');
  }
);
observable.subscribe(
  () => {
    console.log('Second request completed');
  },
  (error: HttpErrorResponse) => {
    console.log('Second request error');
  }
);

控制台

zone.js:2935 GET http://localhost:4200/api/version/secured 401 (Unauthorized)
login.component.ts:54 First request error
zone.js:2935 GET http://localhost:4200/api/version/secured 401 (Unauthorized)
login.component.ts:62 First request error

无关背景
我有一个 LogonService 对象来处理我的所有登录功能。它包含一个布尔变量,显示我是否登录。每当我调用登录函数时,它都会订阅 httpClient.get 的可观察对象,以将登录变量设置为 true 或 false。但是登录函数也是 returns 被订阅的可观察对象。我花了一些时间 link 双重订阅的双重请求。如果有比通过变量跟踪登录更好的方法,请告诉我!我正在努力学习 angular :)

http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html

Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.

您可以对 HttpClient.get 的结果使用 share 运算符,如下所示:

var observable = this.httpClient.get('api/version/secured', { headers: httpHeaders })
  .pipe(share());

您需要在脚本顶部添加以下导入:

import { share } from 'rxjs/operators';

share 运算符生成可观察的 hot,即在订阅者之间共享。但还有很多,我建议 this article 深入研究(当然你也可以 google 向上 hot vs cold observables 找到更多)。

你的 observable 是冷的:

An observable is cold if the producer of its notifications is created whenever an observer subscribes to the observable. For example, a timer observable is cold; each time a subscription is made, a new timer is created.

您需要 multicast 您的 Observable,或者换句话说,让它变热:

An observable is hot if the producer of its notifications is not created each time an observer subscribes to the observable. For example, an observable created using fromEvent is hot; the element that produces the events exists in the DOM — it’s not created when the observer is subscribed.

为此,您可以使用 share 运算符,但它仍然不能保证您进行一次 http 调用。 Share 将 multicast 您的可观察对象,使其在订阅者之间共享,但是一旦 http 调用完成,它将为新订阅者进行新的 http 调用。

如果您想要缓存行为(执行一次调用,然后在每个订阅者订阅时向其提供值),您应该使用 publishReplay().refCount().

进一步阅读:

Publish and share operators

除了上述答案之外,您还可以将您的 http 服务分配给 observable,然后订阅获取数据。例如:

export class App implements OnInit {

lessons$: Observable<Lessons[]>;

constructor(private lessonsService: lessonsService) {

}

ngOnInit() {
    this.lessons$ = this.lessonsService.loadLessons().publishLast().refCount();

    this.lessons$.subscribe(
         () => console.log('lessons loaded'),
         console.error
         );
    }
}

The Angular documentation.