多个httpclient请求,该请求依赖于来自其他使用Observable的httpclient请求的数据

时间:2019-08-20 15:15:11

标签: nested observable angular-httpclient

我正在用angular 5重写一个应用程序,该应用程序从服务器获取数据并下载一些pdf文件。该服务器是具有3个api的soap接口:

1-获取广告系列列表

2-按广告系列ID获取文件数组

3-按文件ID获取文件

我已经使用纯XMLHTTPRequest和一些递归函数来完成此操作,这些递归函数填充了“广告系列”对象,提取了封面并一个接一个地保存了所有文件。

我现在想要得到的是避免回调地狱,并使用httpclient和angular中的observables获得相同的结果,但是在研究了rxjs库两天后,我仍然不知道该怎么做。

我已经通过httpclient成功地获取了数据,并且我对可观察的操作者做了很多尝试。 事件的示意流程为:

获取广告系列xml =>

将xml数据转换为一组广告系列(使用地图运算符完成)=>

对于每个广告系列,获取filesInfo =>

将xml数据转换为filesInfo数组=>

为每个广告系列的每个文件获取该文件=>

提取文件封面并保存文件。

我被困在这里:

getCampaigns():Observable<Campaign[]>{
    return this.getRawDataProvider.getRawData('campaignsList').pipe(
      map(rawCampaigns => {return this.createCampaignArray(rawCampaigns)})
    )
  }
this.getCampaigns().subscribe(result => {
  console.log(result) //correctly shows an array of campaigns (without the files of course)
})

现在怎么办?我应该使用“ from”运算符从数组中创建新的可观察对象吗?还是阵列上的foreach并为每个广告系列启动新的http客户端请求?最后,我需要一系列这样的广告系列:

[
  {
    name: 'campaign name',
    id: 1234,
    files:[
      {
        name:'file name',
        id: 4321,
        cover: 'base64string'
      }
    ]
  }
]

我需要将文件一次又一次下载,而不是并行下载

编辑: 也感谢dmcgrandle的回答,我弄清楚了一些事情,经过几次尝试,我来到了这里:

updateCampaigns():Observable<Campaign[]>{
    let updatedCampaigns:Campaign[];
    return this.getRawDataProvider.getRawData( 'campaignList' ).pipe(
      map( rawCampaigns => {
        updatedCampaigns = this.createCampaignsArray( rawCampaigns );
        return updatedCampaigns;
      } ),
      switchMap( _campaigns => from( _campaigns ).pipe(
        concatMap( _campaign => this.getRawDataProvider.getRawData( 'campaignDocuments', _campaign.id ) )
      ) ),
      map((rawDocuments,i)=>{
        updatedCampaigns[ i ].documents = this.createDocumentsArray( rawDocuments );
        return;
      } ),
      reduce( () => {
        let { campaigns, documentsToDowloadIdList } = this.mergeWithStoredData( updatedCampaigns );
        updatedCampaigns = campaigns;
        return documentsToDowloadIdList;
      } ),
      switchMap( list => from( list ).pipe(
        concatMap( documentId => this.getRawDataProvider.getRawData( 'document', documentId ).pipe(
          map( b64file => { return { documentId:documentId, b64file:b64file } } )
        ) )
      ) ),
      concatMap( file => this.saveFile( file.b64file, file.fileId ) ),
      tap( fileInfo => {
        updatedCampaigns.forEach( campaign => { campaign.documents.forEach( document => {
          if( fileInfo.fileId == document.id ) {
            document.cover = fileInfo.b64cover;
            document.numPages = fileInfo.numPages;
          }
        } ) } )
      } ),
      reduce( () => {
        this.storageservice.set( 'campaigns' , updatedCampaigns );
        return updatedCampaigns;
      } )
    )
  }

请注意,我已经修改了saveFile函数,现在它返回一个可观察的对象。 现在,这可行...我想知道是否有更简单的方法,是否缺少某些东西。 我也在尝试使用catchError运算符处理错误,但是如果我使用throw,则可观察对象的类型将变为空,并且我必须放置类型“ any”而不是“ Campaign []”以使其起作用。 最后一件事,我希望这个可观察的对象每次使用新文档进行更新时都返回campaigns对象,以便在下载文件时显示封面,而不是在最后显示全部……可以这样做吗?

1 个答案:

答案 0 :(得分:1)

我建议您在一个函数中完成所有这些操作。有了一系列广告系列后,一次发射一个项目,并对每个项目进行一次转换,一次发射一次。然后,最后将所有结果放入数组中,并从函数中返回。

在开发过程中,我还将在每个阶段插入tap(),以查看每次转换后发生了什么,以确保其正常运行。

使用伪代码,看起来像这样:

getCovers(): Observable<Cover[]> {
  return this.getRawDataProvider.getRawData('campaignsList').pipe(
    map(rawCampaigns => this.createCampaignArray(rawCampaigns)),
    concatAll(), // emit the array one item at a time
    concatMap(campaign => this.getFileInfo(campaign)), // get FileInfo for each campaign
    concatMap(fileInfo => this.getTheFile(fileInfo)), // get the File for each fileInfo
    concatMap(file => this.saveTheFileAndGetCover(file)), // save the file, return the cover
    toArray() // put the results into an array for returning.
  )
}