角度递归HTTP请求,合并响应,最终返回单个可观察到的

时间:2019-04-04 19:34:34

标签: angular rxjs observable angular-http

在浏览完所有页面之前,我想对分页的API进行递归HTTP调用。每个页面包含一个资产数组,我将这些资产连接到一个临时数组。阅读完所有页面后,我想返回所有资产的单个数组作为可观察值。

我一直在使用管道与expand运算符。我注意到我可以在订阅完成时打印我的最终数组,但是我不知道如何优雅地返回此数组作为可观察的数组。有什么方法等待一个可观察的对象完成然后返回另一个?

getAllAssets(sort: string, filter: AssetFilter): Observable<Asset[]> {
  let allAssets: Asset[] = [];
  console.log('getAllAssets()', sort, filter);
  this._getAllAssets(null, null).subscribe((moreAssets) => {
    allAssets = allAssets.concat(moreAssets);
  }, null, () => console.log(allAssets));
  return of(allAssets);
}

_getAllAssets(sort: string, filter: AssetFilter) {
  let currentPage = 0;
  return this.getAssets(null, null, sort, filter).pipe(
    expand(assetsPage => {
      if (assetsPage && assetsPage.page.number + 1 < assetsPage.page.totalPages) {
        return this.getAssets(currentPage += 1, null, sort, filter);
      }
      return empty();
    }),
    map((value) => value._embedded.assets)
  );
}

getAssets(page: number, size: number, sort: string, filter: AssetFilter): Observable<Assets> {
  let url = 'https://gateway.' + environment.region + '.mindsphere.io/api/assetmanagement/v3/assets?';
  if (page) {
    url += 'page=' + page;
  }
  if (size) {
    url += 'size=' + size;
  }
  if (sort) {
    url += 'sort=' + sort;
  }
  if (filter) {
    url += 'filter=' + JSON.stringify(filter);
  }
  return this.http.get<Assets>(url, {
    headers: new HttpHeaders().set('Content-Type', 'application/json').set('Authorization', 'Bearer ' + this.accessToken)
  });
}

我正在将正确的数组打印到屏幕上,但是我想将此数组作为可观察对象返回,而无需创建可观察对象,返回可观察对象,并在完成时将其发送给它。

编辑: 我的解决方法是使用toArray(),如下所述。然后,我刚刚做了一张地图,将Observable<item[][]>变成了Observable<item[]>

getAllAssets(sort: string, filter: AssetFilter): Observable<Asset[]> {
  let currentPage = 0;
  return this.getAssets(null, null, sort, filter).pipe(
    expand(assetsPage => {
      if (assetsPage && assetsPage.page.number + 1 < assetsPage.page.totalPages) {
        return this.getAssets((currentPage += 1), null, sort, filter);
      }
      return empty();
    }),
    map(value => value._embedded.assets),
    toArray()
  ).pipe(map(assets => [].concat.apply([], assets)));
}

getAssets(page: number, size: number, sort: string, filter: AssetFilter): Observable<Assets> {
  let url = 'https://gateway.' + environment.region + '.mindsphere.io/api/assetmanagement/v3/assets?';
  if (page) {
    url += 'page=' + page;
  }
  if (size) {
    url += 'size=' + size;
  }
  if (sort) {
    url += 'sort=' + sort;
  }
  if (filter) {
    url += 'filter=' + JSON.stringify(filter);
  }
  return this.http.get<Assets>(url, {
    headers: new HttpHeaders().set('Content-Type', 'application/json').set('Authorization', 'Bearer ' + this.accessToken)
  });
}

2 个答案:

答案 0 :(得分:0)

您想要一个toArray运算符

这里有一个timer

的示例
timer(0, 5).pipe(
    take(3),
    toArray()
  )

toArray

答案 1 :(得分:0)

需要花一些代码进行筛选,因此我将对其进行归纳和归纳,因为这是一种常见的模式,它将采用以下形式:

   _getAllPages(page, constantArgs, lastSet) {
     lastSet = lastSet || []; // deal with entry
     return this.getPage(page, constantArgs).pipe( // fetch the page
        switchMap(res => 
          (res.nextPage) // if nextpage, recurse and concat to last set
             ? this._getAllPages(res.nextPage, lastSet.concat(res.items))
             : of(lastSet.concat(res.items)))); // else break
   }

出于样式目的,我通常将其设为私有,而将使用public:

getAllPages(constantArgs) {
  return this._getAllPages(0, constantArgs);
}

使API更好/更易于理解。