如何加载多个休息请求

时间:2019-01-21 12:23:51

标签: angular

我必须执行多个GET请求才能从外部页面加载数据。

a请求的响应可能返回一个标志,指示有更多数据要加载:   “ nextPage”:“ / v1 / catalog / products?page = 2&pageSize = 10”,

下面是我的函数代码。

我试图实现一个do while循环,但无法使其正常工作。我想还有一种更聪明的方式可以做到这一点-也许是Switchmap?

旧版本

  loadCatalog() {
    return new Promise((resolve, reject) => {
        this.http.get<Catalog[]>(ZUORA_URL + '/v1/catalog/products?page=1&pageSize=10', { headers })
              .pipe(map(data => data))
              .subscribe(data => {
                this.catalog = data;
                resolve(true);
            });
    });
}

我想加载完整的数据并将其存储在一个地方。如何循环播放,直到没有其他下一页? -现在可以一页一页地加载,但是我仍在努力存储响应...

更新版本

  getProducts(url, dataSoFar = []): Observable<any[]> {
    if (!url) {
      return of (dataSoFar);
    } else {
      url = ZUORA_URL + url;
    }
    return this.http.get<any>(url, { headers }).pipe(
      switchMap(p => this.getProducts( p.nextPage, [...dataSoFar, ...p.data]))
    );
  }

  getData() {
    return this.getProducts('/v1/catalog/products');
  }

3 个答案:

答案 0 :(得分:0)

我真的不确定是否建议用Promise包装可观察对象并作为副作用更新数据。

loadCatalog(URL) {
    return new Promise((resolve, reject) => {
        this.http.get<Catalog[]>(ZUORA_URL + URL , { headers })
          .pipe(map(data => data))
          .subscribe(data => {
            resolve(data);
        });
    });
}

现在您可以链接请求以获取数据,如下所示

async loadAllCatalogs(URL) {
  return new Promise((resolve, reject) => {
    try { 
    let catalogs = [];
    let data = await this.loadCatalog('/v1/catalog/products?page=1&pageSize=10');
    catalogs.push(data); // store catalog as an array since there may be more results based on nextPage key
    while(data.nextPage) {
       data = await this.loadCatalog(data.nextPage);
       catalogs.push(data);
    }
    resolve(catalogs);
    }
    } catch (e) {
      reject(e);
    }
  });
}

答案 1 :(得分:0)

您可以使用expand递归调用api,并可以使用reduce减少对单个数组的所有响应。

在您的Service(MyService)中:

 
import { EMPTY } from 'rxjs';
import { expand, reduce, map } from 'rxjs/operators';

baseUrl = ZUORA_URL;

// Let's say you get an object like this from your API
interface ApiResponse {
  nextPage: string,
  data: any[]
}

public fetchData(apiEndpoint: string): Observable<any[]> {
  return this.http.get<ApiResponse>(baseUrl + apiEndpoint, { headers })
    .pipe(
      // recursively call the GET requests until there is no further 'nextPage' url
      expand(apiResponse => {
        if (!apiResponse.nextPage) {
          return EMPTY;
        }
        return this.http.get<ApiResponse>(apiResponse.nextPage, { headers });
      }),
      // map the api response to the data we actually want to return
      map(apiResponse => apiResponse.data),
      // reduce the data of all GET requests to a single array
      reduce((accData, data) => accData.concat(data))
    )
}

在您的组件中:

private products: Product[];

loadProducts() {
  this.myService.fetchData('/v1/catalog/products').subscribe(products => 
    this.products = products as Product[]
  )
}

答案 2 :(得分:0)

通常,在对页面进行分页时,您只希望在用户主动请求时才获取它们。但是,我将直接回答您的问题,而把演讲留在外面。

我不确定在期望将数组作为响应中的顶级数组时如何包含nextPage,因此,我将假定响应实际上是以下形式:< / p>

interface CatalogResponse {
  nextPage: string | undefined;
  products: Catalog[];
}

为此,您可以使用Observable

public loadProducts(url: string): Observable<Catalog[]> {
  let nextPage: Subject<string> = new Subject<string>();
  let products: Subject<Catalog[]> = new Subject<Catalog[]>();

  nextPage.subscribe((url: string) => {
    this.fetchProducts(url, products, nextPage);
  }).add(() => products.complete());

  return products;
}

private fetchProducts(url: string, products: Subject<Catalog[]>, nextPage: Subject<string>): void {
  this.http.get<CatalogResponse>(url, { headers })
          .subscribe(response => {
            products.next(response.products);
            if (response.nextPage) {
              nextPage.next(response.nextPage);
            } else {
              nextPage.complete();
            }
          });
}

如果要无限期地进行操作,您需要确保执行“取消”或“停止”操作。

相关问题