Angular 6:如何并行进行一组服务调用,并在它们全部完成后处理数据

时间:2018-11-02 09:28:37

标签: angular rxjs observable angular6 rxjs6

我正在使用rxjs 6+开发Angular 6+ Web应用程序。

有一组服务(所有服务都不同,并且会进行不同的http调用)。 基本上,这些服务用于初始化应用程序。

我的要求是并行调用这些服务,并等到它们全部完成为止。完成后,我需要处理收到的回复。 我对此进行了很多研究,发现forkJoin和CombineLatest可以成为我的朋友。 而且我已经通过以下方式实现了相同的方法:

 getInitialData() {

 this._authService.openCorsConnection().subscribe(res => { 
  forkJoin(
    this._authService.x(),
    this._tService.getSystemDate(),
    this._tService.getTemp(),
    this._tService.getSettings()
  ).pipe(
    catchError( err => {
      console.log('gor error',err);
      if (err.status == 401) {
          // this._router.navigateByUrl('/unauthorize');
          return EMPTY;
      } else {
          return throwError(err);
      }
 }),
    map(([x,y,z,p])=>{
           console.log('sucesss',{x,y,z,p});          
            return {x,y,z,p}
    }),
  finalize(()=>{
        console.log('Executing Finally');
        processData();

  })
  );
});
 }

但是这些没有执行。以下是服务:

x(): Observable<any> {          
const xUrl  = EnvironmentConfiguration.endPointConfig.xServiceEndpoint;
          let serviceMethod: String = 'x';
          let requestObj: any = this._utilHelperSvc.getRequestObject(xUrl  , { "y": "" }, serviceMethod);
          return this._http.post<any>(requestObj.url,{ "pid": "" } , requestObj)
          .pipe(
            catchError(this.handleError('x', {}))
          );
      }

我需要修改服务吗? 我不知道如何解决这个问题。

有人可以为此建议一些更好的解决方案或其他方法吗?

谢谢。

2 个答案:

答案 0 :(得分:2)

TL; DR

使用zipforkjoin

演示:

说明

您无需等待forkJoin处理响应,反之亦然。相反,准备所有需要完成的工作,然后等待完成。

这是我的意思:

let process1$ = timer(1000);
let process2$ = timer(2000);
let process3$ = timer(3000);

const doSthg1 = pipe(
  tap(() => console.log('Process 1 has finished')),
  map(() => 'Process 1 has finished')
)

const doSthg2 = pipe(
  tap(() => console.log('Process 2 has finished')),
  map(() => 'Process 2 has finished')
)

const doSthg3 = pipe(
  tap(() => console.log('Process 3 has finished')),
  map(() => 'Process 3 has finished')
)

forkJoin(doSthg1(process1$), doSthg2(process2$), doSthg3(process3$)).subscribe(() => {
  console.log('Now I am complete');
});

Demo with forkJoin

只要您的进程未链接,即一个输入不依赖于另一个输出,此方法就起作用。

我的代码为什么不起作用?

因为您实际上没有订阅forkJoin。 解决方案:例如,您可以使用concatMapthis._authService.openCorsConnection()转换为另一个Observable:

getInitialData(){
  this._authService.openCorsConnection().pipe(
    concatMap(() => forkJoin(this._authService.x(),
      this._tService.getSystemDate(),
      this._tService.getTemp(),
      this._tService.getSettings()
    )),
    catchError(err => {
      console.log('gor error', err);
      if (err.status == 401) {
        return EMPTY;
      } else {
        return throwError(err);
      }
    }),
  ).subscribe(() => processData());
}

zip

在以下情况下,您可以使用zip代替forkJoin

  1. 所有进程仅发出一次,然后完成:例如http Get或Post请求。
  2. 您要从源中获取联接输出。

实现:

zip(doSthg1(process1$), doSthg2(process2$), doSthg3(process3$))
.subscribe(
      ([x,y,z]) => {
      console.log(`x=${x}, y=${y}, z=${z}`);
});

Demo with zip

答案 1 :(得分:0)

几天前,我经过大量搜索。

最主要的是如何调用API列表。

如果您从 component.ts 调用API,那么您将获得其中的数据。不同之处在于,当您通过服务调用API时,必须存储API调用的引用

我已经通过这种方式完成了...

var pendingAPIList : any = [];

您可以将api列表添加到具有可观察参考的数组中。

return new Observable(observer => {
    var obj = {
       url:url,
       param:param,
       ref : observer  // <-- Hear is the main trick 
    }
    this.pendingAPIList.push(obj);  // Add multiple API in array
})

添加了所有API后,然后调用...

Observable.forkJoin(this.pendingAPIList).subscribe(response){

     // You will get data in array 
     // Now return all API res in their subscribers at .ts file... 

     for(let i = 0 ; i < this.pendingAPIList.lengh ; i++  ){
        this.pendingAPIList[i].ref.next(response[i]);
        this.pendingAPIList[i].ref.complete();   
     }
}

这对我来说很好...:)