【问题标题】:deferred Rxjs BehaviorSubject mechanism延迟 Rxjs BehaviorSubject 机制
【发布时间】:2018-08-31 22:19:10
【问题描述】:

我有以下要求。

我有一个带有 BehaviorSubject 的 Angular 服务。 完成了一个 http 请求,完成后,将使用该值调用 BehaviorSubject.next 方法。 该值可以在单个页面的生命周期内发生变化。

不同的订阅者注册到它,并在它发生变化时被调用。

问题在于,当 http 请求处于挂起状态时,BehaviorSubject 已经包含一个默认值,而订阅者已经立即获得了这个值。

我想要的是订阅者必须等到 http 请求完成(延迟)并在 http 请求完成时获取值并设置值。 所以我需要的是某种延迟行为主体机制。

我将如何使用 rxjs 实现这一点?

另一个要求是,如果我在某个方法中订阅行为主体,我们希望订阅者获得第一个非默认值并且订阅结束。我们不希望重新执行函数中的本地订阅。

【问题讨论】:

  • 为什么不直接暴露http调用返回的Observable,需要经过一个BehaviorSubject呢?
  • 听起来你可以切换到ReplaySubject(1),就是这样。
  • 实际上 http 调用并没有在 BehaviorSubject 中创建值。它执行用于身份验证的后端调用,创建一个 cookie(遗留代码),当 http 调用完成后,我们根据 cookie 中的信息组成一个对象。此外,由于应用程序中的一些其他非 http 操作,该对象的值可能会发生变化(不再有 http 请求)。
  • 订阅者不知道值是由于 http 请求还是非 http 操作而发生变化。他们只需要获取最新的值。
  • 我在玩 ReplaySubject。我还想要的是,如果在一个方法中订阅了主题:我们等到第一个值被发出,然后我们取消订阅以防止方法中的订阅被一遍又一遍地调用。

标签: rxjs rxjs5


【解决方案1】:

对您的行为主题使用过滤器,这样您的订阅者就不会获得第一个默认发出的值:

mySubject$: BehaviorSubject<any> = new BehaviorSubject<any>(null);

httpResponse$: Observable<any> = this.mySubject$.pipe(
  filter(response => response)
  map(response => {
     const modifyResponse = response;
    // modify response
    return modifyResponse;
  }),
  take(1)
);
this.httpResponse$.subscribe(response => console.log(response));

this.myHttpCall().subscribe(response => this.mySubject$.next(response));

如果需要,您当然可以将 httpResponse$ 可观察对象包装在方法中。

【讨论】:

  • 我明白你的意思。但是我们还想要的是,当我们得到第一个发出的值(非 null)时,我们想要关闭订阅,以防止函数中的闭包被一遍又一遍地调用。
  • 因此,在您的组件中,就在订阅数据之前,您执行this.httpRequestObservable$.pipe(take(1)).subscribe();,因此您将只获得一个 http 调用。这就是你想要达到的目标吗?
  • 程序的工作方式如下: 1a) 对身份验证进行 http 调用 1b) 并行调用订阅主题的方法 -> 此方法必须等到 http 请求完成才能获取值 2 ) 当 http 调用完成时,该值被填入主题 3) 稍后在代码中调用一些 js 逻辑来更改主题中的值 -> 不应再调用方法 1b 中的订阅
  • 我认为你的 httpCall 是一个承诺?在 then 方法中,您可以推送主题中的数据并执行映射以更改值。我将编辑我的答案。
  • http 调用也是可观察的,因此使用 subscribe 而不是 then。我不需要映射,因为主题总是包含给定格式的对象??
【解决方案2】:

我认为您想推迟发出的默认值这一事实,立即引发了您为什么要使用 BehaviorSubject 的问题。让我们记住:使用 BehaviorSubject(而不是 Subject 或普通的 Observable)的主要原因是立即向任何订阅者发出值。

如果你需要一个 Observable 类型,你需要控制生产者(通过 .next([value]))和/或你想要开箱即用的订阅多播,那么 Subject 是合适的.

如果除此之外还有一个额外要求是订阅者需要立即获得一个值,那么您需要考虑 BehaviorSubject

如果您没有说需要从其他非 http 事件/源更新值,那么我会建议使用 shareReplay(1) 模式。不过……

private cookieData$: Subject<RelevantDataType> = new 
Subject<RelevantDataType>(null);


// Function for triggering http request to update
// internal Subject.
// Consumers of the Subject can potentially invoke this 
// themselves if they receive 'null' or no value on subscribe to subject
public loadCookieData(): Observable<RelevantDataType> {
    this.http.get('http://someurl.com/api/endpoint')
        .map(mapDataToRelevantDataType());
}

// Function for dealing with updating the service's 
// internal cookieData$ Subject from external 
// consumer which need to update this value
// via non-http events
public setCookieData(data: any): void {
    const newCookieValue = this.mapToRelevantDataType(data); // <-- If necessary
    this.cookieData$.next(newCookieValue); // <-- updates val for all subscribers
}

get cookieData(): Observable<RelevantDataType> {
    return this.cookieData$.asObservable();
}

该解决方案基于 OPs cmets 等。 - 处理订阅主题类型。 - 处理无法直接“下一个”新值的外部订阅者 - 处理能够在主题类型上设置新值的外部生产者 - 处理在 http 请求挂起时不提供默认值

【讨论】:

    猜你喜欢
    • 2018-10-17
    • 2021-04-10
    • 1970-01-01
    • 2017-07-27
    • 2017-05-15
    • 2017-05-04
    • 2021-01-31
    • 2020-03-20
    • 2017-08-12
    相关资源
    最近更新 更多