【问题标题】:Angular BehaviorSubject events emited multiple timesAngular BehaviorSubject 事件多次发出
【发布时间】:2019-12-14 20:05:18
【问题描述】:

我对 RxJS observable 有一个奇怪的行为,我不知道为什么。

我有一个服务,它公开 BehaviorSubject 并发出事件:

@Injectable({
    providedIn: 'root'
})
export class SearchService {
    private searchSubject: BehaviorSubject<SearchQueryParams>;

    updateSearch(search: any) {
        this.searchSubject.next(search);
    }

    onSearchUpdate(): Observable<any> {
        return this.searchSubject.asObservable();
    }
}

在我的组件中,我使用shareReplay() 订阅了这个Observable,因为我在模板中使用了多个async 管道:

@Component({
    selector: 'rp-produit-recherche',
    template: '<div class="search">
    <app-facets class="facets" [facets]="searchResult$ | async"></app-facets>
    <app-results [results]="searchResult$ | async"></app-results>
</div>
',
    styleUrls: ['./produit-recherche.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class SearchComponent implements OnInit {

   public searchResult$: Observable<any>;

   ...

   ngOnInit() {
        this.searchResult$ = this.SearchService.onSearchUpdate().pipe(
            switchMap((searchRequest) => {
                return this.apiService.search(searchRequest);
            }),
            shareReplay()
        );
    }

}

这实际上工作正常。当一个事件发出时,调用 api 并且结果就是我想要的。

现在我添加了第二个 Route 服务于相同的组件。我的问题已经开始了。

当我从路线 a 导航到路线 b 时。事件仍然被发出,但在订阅中它被接收了两次。如果我在这些路线之间再次导航,我会得到 n 个事件 + 1,等等。

我的路线:

    {
        path: '',
        children: [
            {
                path: '',
                resolve: {
                    searchQueryParams: SearchQueryParamsResolverGuard
                },
                children: [
                    {
                        path: 'a',
                        component: SearchComponent,
                    },
                    {
                        path: 'b',
                        component: SearchComponent
                    }
                ]
            }
        ]
    }

以及发出事件的解析器:

@Injectable({
    providedIn: 'root'
})
export class SearchQueryParamsResolverGuard implements Resolve<any> {

    constructor(private searchService: SearchService) {
    }

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): SearchQueryParams {
        const search = route.queryParamMap.get('search');
        return this.searchService.updateSearch(search);
    }
}

我不明白为什么会这样。导航时组件被破坏,因此订阅应该已经结束。

我希望有人已经遇到此问题并找到解决方法。谢谢

【问题讨论】:

  • 何时调用this.SearchService.updateSearch()
  • 在几个地方,但在这种情况下在SearchQueryParamsResolverGuard resolver
  • 我可能找到了问题的根源:shareReplay() 如果我删除它,问题就解决了,但在模板中Observable 是在多个地方订阅的。但我只需要调用一次 api 并共享数据。 share() 似乎更适应。我需要了解差异。
  • 请尝试shareReplay(1)@MartinChoraine
  • @MartinChoraine,请检查更新的答案

标签: angular rxjs observable


【解决方案1】:

这里的问题是因为shareReplay,(in detail

原因: shareReplay 操作符在流尚未完成时不会清理它们。这将在我们的应用程序中引入内存泄漏。

shareReplay({
    bufferSize: 1,
    refCount: true
})

WORKING DEMO问题 + 解决方案)检查product.component.ts


其他人可能就是这种情况:

你只需要在ngOnDestroyunsubscribe这个ngOnDestroy

ngOnDestroy() {
    this.searchResult$.unsubscribe();
}

原因:

这里发生的是它多次订阅同一个 observable 和 它永远不会被破坏,所以如果您通过您的站点并返回 到同一个页面一次又一次地订阅一次(我得到了一个 n 事件 + 1),所以当发出事件时 发生一次,但现在有多个侦听器。

【讨论】:

  • 我无法取消订阅,因为我正在使用 async 管道,所以 Angular 处理为我订阅和取消订阅。
  • @MartinChoraine,如果是这样,那么这不应该发生,只需尝试一下并检查它是否有效
  • 行为相同
  • @MartinChoraine,能否分享一下 SearchQueryParamsResolverGuard 的代码?
  • @MartinChoraine,太好了,我也学到了新东西,谢谢你。
【解决方案2】:

我明白发生了什么:

我需要了解更多关于multicast/share/publish 运营商的信息。

问题来自我的shareReplay() 用法。我只需要分享最后一个事件。所以我必须添加配置:

shareReplay({
    bufferSize: 1,
    refCount: true
})

bufferSize : 1 将仅发出最后一个事件
refCount: true 将自动连接和断开可观察可连接对象的过程

对于我的使用,我可以使用share() 而不是shareReplay()

如需更多信息(我不是 100% 有信心,请随时纠正我。

shareReplay({bufferSize: 1, refCount: true)) == share()
shareReplay({bufferSize: 1, refCount: false)) == shareReplay(1)
shareReplay({refCount: false)) == shareReplay()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-15
    • 2018-11-18
    • 2021-12-31
    • 2019-10-30
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    相关资源
    最近更新 更多