【问题标题】:How to control the flow of service calls, using rxjs with Angular 9?如何使用 rxjs 和 Angular 9 控制服务调用的流程?
【发布时间】:2020-08-25 06:04:49
【问题描述】:

我的代码如下所示:

userContext: UserContext;
  userContext$ = this.http.get<UserContext>("assets/api/userContext.json", {}).pipe(
      shareReplay(1),
      tap((val: UserContext) => {
        this.userContext = val;
        console.log(this.userContext);
      })
  );
  userAnnouncements$ = this.http.post<Announcement>(this.userAnnouncementsService,
    {
      countryCode: this.userContext.countryCode, 
      companyGroupId: this.userContext.companyGroupId,
    }
  );

我们可以清楚地看到第二个服务 (userAnnouncements$) 需要第一个服务 (userContext$) 接收到的值,因为它们是异步的,所以我的 userContext: UserContext;执行第二个服务时为空。

我还需要 userContext.companyCode 和 GroupId 以供将来调用,因此我需要一种方法将其保存在某个地方以供后续调用。

我尝试了解析器和 APP_INT... 但后来意识到这不是两个地方的正确目的。

【问题讨论】:

  • 您的userContext 只被提取一次?如果是这样,您可以将其视为单例。
  • @VRoxa 是的,获取一次并用于所有后续调用,我猜谜题是关于如何\何时在进行其他调用之前获取它。

标签: angular typescript async-await rxjs observable


【解决方案1】:

不确定这是否是您要求的,但您可以使用 switchMap

userContext: UserContext;
  userContext$ = this.http.get<UserContext>("assets/api/userContext.json", {}).pipe(
      tap((val: UserContext) => {
        this.userContext = val;
        console.log(this.userContext);
      }),
      switchMap((val: UserContext) => {
          return this.http.post<Announcement>(this.userAnnouncementsService, 
          {
              countryCode: val.countryCode,
              companyGroupId: val.companyGroupId
          });
      }
  );

那么你只有一个订阅

【讨论】:

    【解决方案2】:

    你已经在使用shareReplay,你唯一要做的就是在任何你想要的地方使用这个 Observable 的结果。

    userAnnouncements$ = this.userContext$.pipe(mergeMap((userContextData) => {
        return this.http.post<Announcement>('someUrl',
            {
            countryCode: userContextData.countryCode, 
            companyGroupId: userContextData.companyGroupId,
            }
        );
    }));
    

    【讨论】:

    • 我认为这确实是最干净的方法,无论我引用多少次\使用 this.userContext$.pipe 中的数据它只会执行一次然后提供数据分享回复。现在尝试实现它以检查是否有任何故障。
    【解决方案3】:

    我期待你的回答。

    如果你 UserContext 是它应该被调用一次的东西,正如我所提到的,你可以把它想象成一个单例。
    获取一次,然后将其缓存在您的服务中。

    class MyService {
    
        private _userContext: UserContext | undefined;
    
        constructor (private http: BackgroundService) { }
    
        userContext$ = this.http.get<UserContext>(`url`).then(ctx => {
            console.log(ctx);
            this._userContext = ctx;
            return ctx;
        });
    
        userAnnouncements$ = (): Promise<Announcement> => 
            this._userContext
                ? this.userAnnouncement(this._userContext)
                : this.userContext$.then(ctx => this.userAnnouncement(ctx));
    
        private userAnnouncement = (ctx: UserContext): Promise<Announcement> =>
            this.http.post<Announcement>(`url`, {
                countryCode: ctx.countryCode,
                companyGroupIp: ctx.companyGroupIp
            });
    }
    
    interface BackgroundService { // aka HttpClient
        get: <TResult>(url: string) => Promise<TResult>;
        post: <TResult>(url: string, body: any) => Promise<TResult>;
    }
    

    我只是模拟了HttpClient,只是为了让我正确编译它。你可以忘记它。

    关键是,你的服务类有一个内部属性UserContext。对userContext$ 的任何调用都将提供此对象并在服务内缓存。
    如果对userAnnouncements$ 的调用完成,则它使用缓存的UserContext。每当对userAnnouncements$ 的调用在对userContext$ 的任何调用之前完成,所以UserContextnullundefined,那么,之前对userContext$ 的调用是完成该属性。

    _userContext 可能是静态的,具体取决于您的依赖注入策略。如果你想让每个服务都存储自己的_userContext,很好。即使MyService 是单例的,那么_userContext 也不应该是静态的。否则,需要一个静态的_userContext 来在所有服务实例之间共享。

    此外,您可以重载 userAnnouncements$ 以强制重新获取 UserContext。如果需要,那会很有趣。

    希望对你有帮助。

    【讨论】:

    • 这是一种简单的方法,但由于他已经在使用shareReplay,他不需要这样做。请看这个例子:stackblitz.com/edit/… 打开控制台查看日志,打开网络选项卡查看 XHR 请求,每次点击都会看到日志,但 XHR 只会看到一次。
    • 即使两次调用userAnnouncements$,您是否也执行一次UserContext 调用?
    • 是的,没错。如添加的示例所示,我只调用一次 API,与单击按钮多少次无关。
    • @VRoxa 是的 ShareReplay 为您做所有事情(与缓存相关),因此允许您编写更清晰的代码。
    猜你喜欢
    • 1970-01-01
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-18
    • 2019-11-06
    • 2018-12-14
    相关资源
    最近更新 更多