首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >角度递归HTTP请求,组合响应,最终返回单个可观察对象

角度递归HTTP请求,组合响应,最终返回单个可观察对象
EN

Stack Overflow用户
提问于 2019-04-05 03:34:34
回答 2查看 283关注 0票数 1

我想对分页的API进行递归HTTP调用,直到遍历完所有页面为止。每个页面包含一个资源数组,我将把它们连接到一个临时数组中。一旦我读完了所有的页面,我想返回一个包含所有资产的数组作为一个可观察对象。

我一直在使用带有扩展操作符的管道。我注意到我可以在订阅完成时打印我的最终数组,但我不知道如何优雅地将此数组作为可观察对象返回。是否有某种方法可以等待一个观察值的完成,然后返回另一个观察值?

代码语言:javascript
复制
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[]>

代码语言:javascript
复制
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)
  });
}
EN

回答 2

Stack Overflow用户

发布于 2019-04-05 05:31:47

您需要一个toArray运算符

下面是一个包含timer的示例

代码语言:javascript
复制
timer(0, 5).pipe(
    take(3),
    toArray()
  )

票数 0
EN

Stack Overflow用户

发布于 2019-04-05 06:57:38

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

代码语言:javascript
复制
   _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
   }

出于风格的目的,我通常会让它成为私密的,并且我会有一个公共的,比如:

代码语言:javascript
复制
getAllPages(constantArgs) {
  return this._getAllPages(0, constantArgs);
}

使API更好/更容易理解。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55523675

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档