可观察到事件源的多个订阅者-异步管道保持订阅和取消订阅

时间:2020-06-01 22:03:57

标签: angular

对我来说有点head头

基本上,我正在实现一个简单的角度应用程序,以显示名称并将其重新分配给ID。

 export class Assignment {
   name: string;
   id?: string;
 }

为此,后端发布端点:

GET /assignments
> [{"1": "id-a"},{"2": ""}] // where "2" is unassigned

PUT /assignments/:name/:id

GET /stream
> text/event-stream of new assignments as type="update", data = {"name": "1", "id":"id-a"} ...

前端具有公开所有这些终结点的服务,流的设置如下:

export class HttpService {
  private source: EventSource;

  constructor(private http: HttpClient, private zone: NgZone) {
    this.source = new EventSource('/stream');
  }
  ...
   public getStream(): Observable<Assignment> {
    return  new Observable(observer => {
      console.log('subscribed to stream');
      this.source.addEventListener('update',(evt: MessageEvent) => 
          this.zone.run(() => observer.next(JSON.parse(evt.data))));
      this.source.onerror = evt => observer.error(evt);
      return () => { console.log('unsubscribing'); }
    });
  }
}

我创建了一个组件来非常简单地保存一项任务

template: `<div> {{assignment.name}} = {{assignment.id}} <div>`
@Input() assignment: Assignment

现在抓到了,我的应用程序组件设置如下:

模板:

 <app-assignment *ngFor="let item of assignments" [assignment]="getUpdatesForName(item) | async"></app-assignment >

组件:

export class MainComponent implements OnInit {

  public selected: string;
  public assignments: Assignment[];
  public update$: Observable<Assignment>;

  constructor(private httpservice: HttpService) {
  }

  ngOnInit() {
    this.httpservice.getAssignments().subscribe( it => this.assignments = it);
    this.update$ = this.httpservice.getStream();
  }

  public selectSound(soundid: string) {
    this.selected = soundid;
  }


  public getUpdatesForName(assignment: Assignment): Observable<Assignment> {
    return concat(
      of( assignment ),
      this.update$.pipe(
        filter(it => it.name=== assignment.name)
      )
    );
  }
}

问题在于,当我通过其余API更改工作分配时,UI上再也没有更新。 取而代之的是,日志稳定地填充了“已订阅”和“未订阅”消息。 因此,异步管道似乎一遍又一遍地进行订阅和取消订阅,而且从未完成任何事情。

我的第一个假设是,总是在源中添加新的侦听器会阻塞某些内容,因此我尝试持有一组订阅者和一个消息侦听器

   export class HttpService {
      private source: EventSource;
      private subscribers :Subscriber<Assignment>[] = [];

      constructor(private http: HttpClient, private zone: NgZone) {
        this.source = new EventSource('/stream');
        this.source.addEventListener('update',(evt: MessageEvent) => this.zone.run(() => this.subscribers.forEach(it => it.next(JSON.parse(evt.data)), false)));
        this.source.onerror = evt => console.log('error in stream' + evt)
      }
    ...
     public getStream(): Observable<Assignment> {
        return  new Observable(observer => {
          console.log('subscribed to stream');
          this.subscribers.push(observer)
          return () => {
           this.subscribers = this.subscribers.filter(it => it !== observer)
          };
        });
      }

没有任何改变。

当我通过传递Observable并在子组件的subscribe(input => localmember = input)中进行OnInit替换异步管道时,整个工作按预期进行

编辑:

我进行了一些测试,发现使用重播主题使其可以准确地处理适合重播缓冲区的邮件数量。

因此,我假设每次CD周期发生时异步管道都会重新订阅,并且在结束时取消订阅?但是,如何知道CD末尾取消订阅的新项目何时发出?

1 个答案:

答案 0 :(得分:0)

好,知道了,但是我可以看到有人遇到了这个问题,不想删除我的问题。

基本上,感谢https://www.lucidchart.com/techblog/2019/10/14/angulars-async-pipe-intricacies/

它说:

虽然不是本文的重点,但这里还有另一个重要说明 是异步管道将仅返回从 如果输入仍然是完全相同的Promise实例,则承诺。如果 您正在调用异步方法或创建新Promise的方法 每次调用时,异步管道都会假定它的任何值 具有不再相关,因此可能永远不会产生可用的 值。

按照我在CD上进行设置的方式,角度将重新评估子组件[assignment]="getUpdatesForName(item) | async"的输入。

这是因为我要通过其过滤的过滤器将始终返回新的Observable,从而导致恒定的sub-unsub模式,并且永远不会产生可用的值。

解决方案是一次创建所有过滤的Observable,然后将它们存储到Map>。