Комбинируйте самые быстрые и возвращаемые значения

#angular #typescript

Вопрос:

В первом примере у меня есть компонент, который вызывает этот клиентский сервис. Сначала мы вызываем loadClientMetrics, чтобы заполнить свойства, но затем берем свойства и немедленно отправляем их в функцию calculateClientInsightsMetrics. Однако я хотел бы изменить службу, как во втором примере, чтобы использовать свойства в службе без необходимости вытаскивать их, чтобы просто передать их обратно. Ниже приведено то, что я хотел бы сделать, однако combineLatest не возвращается до возвращаемого значения, и возвращается значение undefined. Кто-нибудь может мне помочь? Я ищу способ завершить выполнение combineLatest до того, как будет возвращено мое возвращаемое значение.

Основная проблема в том, что у меня есть синхронный метод, который выполняет асинхронные вызовы.

   export class ClientService {

  private metrics = new BehaviorSubject<ClientMetrics>(undefined);
  public metrics$ = this.metrics.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
  private clients = new BehaviorSubject<Client[]>(undefined);
  public clients$ = this.clients.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
  private clientsBySHID = new BehaviorSubject<Client[]>(undefined);
  public clientsBySHID$ = this.clientsBySHID.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
  
  ...
  
  public loadClientMetrics(forceReload: boolean = false) {
    this.loadMetrics(forceReload);
    this.loadClients(forceReload);
    this.loadClientsByShareholderID(forceReload);
  }
  
  ...
  
  public calculateClientInsightsMetrics(metrics: ClientMetrics[], clients: Client[], kycClients: Client[], clientFilter: string): ClientInsightsMetric[] {
      
      let metrics = new Array<ClientInsightsMetric>();
      
    ...Do stuff
    
    return metrics;
      
  }

}
 
    export class ClientService {

  private metrics = new BehaviorSubject<ClientMetrics>(undefined);
  public metrics$ = this.metrics.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
  private clients = new BehaviorSubject<Client[]>(undefined);
  public clients$ = this.clients.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
  private clientsBySHID = new BehaviorSubject<Client[]>(undefined);
  public clientsBySHID$ = this.clientsBySHID.asObservable().pipe(distinctUntilChanged(), shareReplay(1));
  
  ...
  
  public loadClientMetrics(forceReload: boolean = false) {
    this.loadMetrics(forceReload);
    this.loadClients(forceReload);
    this.loadClientsByShareholderID(forceReload);
  }
  
  ...
  
  public calculateClientInsightsMetrics(clientFilter: string): ClientInsightsMetric[] {
      
      let metrics = new Array<ClientInsightsMetric>();
      this.loadClientMetrics(false);

      combineLatest([
      this.metrics$,
      this.clients$,
      this.clientsBySHID$
    ]).subscribe(data => {
        
      ...Do stuff with the data[0], data[1], and data[2]
      
    });
    
    return metrics;
      
  }

}
 

Комментарии:

1. Не уверен, что понимаю, о чем вы спрашиваете. Вы хотите, чтобы обратный вызов подписки был завершен перед выполнением return metrics ?

2. Да, в основном это моя проблема.

Ответ №1:

Похоже, что обратный вызов, переданный subscribe ( data => { .... ), не выполняется синхронно во calculateClientInsightsMetrics время выполнения. Это может выглядеть примерно так:

  1. Вызов calculateClientInsightsMetrics
  2. Позвоните combineLatest и передайте обратный вызов абонента через subscribe возвращенный наблюдаемый. Обратите внимание, что ни одна из работ, определенных в обратном вызове вашего абонента, еще не выполнена.
  3. Возврат metrics (еще не назначен!)
  4. Некоторое ненулевое время спустя событие подписки происходит и metrics назначается (слишком поздно) в обратном вызове подписчика.

Другими словами, вам нужен способ принудительного синхронного выполнения (проверьте combineLatest документацию), или, что более вероятно, вам потребуется «Обещать» свою подписку (обратите внимание, что для этого требуется асинхронное выполнение).:

 public async calculateClientInsightsMetrics(clientFilter: string): ClientInsightsMetric[] {
      
    let metrics = new Array<ClientInsightsMetric>();
    this.loadClientMetrics(false);

    metrics = await new Promise(resolve => combineLatest([
      this.metrics$,
      this.clients$,
      this.clientsBySHID$
    ])
    .subscribe(data => {
      // do what you need to do and call resolve(), passing the value for metrics
      resolve(data);
    }));

    return metrics;
}
 

Обратите внимание, что это противоречит всей цели подписки: вы сможете использовать только первое/следующее значение, переданное вашему абоненту для обратного вызова.