订阅中有角订阅:视图中不会同时加载数据

时间:2019-08-01 10:14:59

标签: javascript angular typescript rxjs

我知道在subscribe中调用subscribe是不好的做法,但是我不知道如何用我的特殊情况来处理它。 现在的代码可以正常工作,但是我的问题是,例如,如果我每秒更新一次网站,则会首先加载表的某些部分,然后再加载其他部分(我的订阅中子目录的内容)。

我有一个包含一个函数的服务,该函数返回可观察到的针对不同资产的文件列表。 在该函数中,我通过调用另一个服务来请求每个资产的文件列表,并且该服务返回可观察值。 然后,我遍历该列表的元素并构建我的数据结构,以便稍后在(AssetFilesTableItems)上返回它们。 有些文件可以是zip文件,我想通过订阅另一个服务(extractZipService)来获取那些文件的内容。为了获得正确的数据,我需要通过请求文件列表获取的文件名。然后,我将zip内容的一些数据添加到AssetFilesTableItems中,并在最后返回所有内容。

该函数的代码如下:

  getAssetfilesData(assetIds: Array<string>, filter: RegExp, showConfig: boolean): Observable<AssetFilesTableItem[][]> {
    const data = assetIds.map(assetId => {
      // for each assetId
      return this.fileService.getFileList(assetId)
        .pipe(
          map((datasets: any) => {
            const result: AssetFilesTableItem[] = [];

            // iterate over each element
            datasets.forEach((element: AssetFilesTableItem) => {

              // apply regex filter to filename
              if (filter.test(element.name)) {
                this.logger.debug(`Filter ${filter} matches for element: ${element.name}`);

                // build up AssetFilesTableItem
                const assetFilesItem: AssetFilesTableItem = {
                  name: element.name,
                  type: element.type,
                  asset: assetId
                };

                // save all keys of AssetFilesTableItem
                const assetFilesItemKeys = Object.keys(assetFilesItem);

                // if file is of type ZIP, extract 'config.json' from it if available
                if (showConfig && element.type.includes('zip')) {
                  this.extractZipService.getJSONfromZip(assetId, element.name, 'config.json')
                    .subscribe((configJson: any) => {
                      const jsonContent = JSON.parse(configJson);
                      const entries = Object.entries(jsonContent);
                      entries.forEach((entry: any) => {
                        const key = entry[0];
                        const value =  entry[1];
                        // only add new keys to AssetFilesTableItem
                        if (!assetFilesItemKeys.includes(key)) {
                          assetFilesItem[key] = value;
                        } else {
                          this.logger.error(`Key '${key}' of config.json is already in use and will not be displayed.`);
                        }
                      });
                    });
                }
                result.push(assetFilesItem);
              }
            });

            return result;
          }));
    });

    // return combined result of each assetId request
    return forkJoin(data);
  }
}

我使用组件中的以下代码更新表:

  getValuesPeriodically(updateInterval: number) {
    this.pollingSubscription = interval(updateInterval)
      .subscribe(() => {
        this.getAssetfilesFromService();
      }
    );
  }

getAssetfilesFromService() {
    this.assetfilesService.getAssetfilesData(this.assetIds, this.filterRegEx, this.showConfig)
    .subscribe((assetFilesTables: any) => {
      this.assetFilesData = [].concat.apply([], assetFilesTables);
    });
  }

编辑:我尝试了ForkJoin,但据我了解它用于并行执行更多请求。我的extractZipService取决于我从fileService获得的结果。另外我已经在末尾有一个forkJoin,它应该结合我对不同资产的所有fileList请求。我不明白为什么当时无法立即加载我的视图。

编辑:问题似乎是在我的fileService订阅的forEach中订阅了extractZipService。似乎在fileService订阅之后完成。我已经尝试了很多东西,例如SwitchMap,mergeMap和此处建议的解决方案,但是没有运气。我确信有可能使它以某种方式工作,但我的想法已经用完了。任何帮助,将不胜感激!

1 个答案:

答案 0 :(得分:1)

您正在for循环内调用this.extractZipService.getJSON。因此,此方法称为asynch,并且map中的函数不等待结果。当结果确实出现时,因为您所查看的项目相同,因此它们会刷新。

要解决此问题,您需要从this.extractZipService.getJSON返回并映射结果,这将为您提供结果的集合,然后对结果执行forkJoin(不确定为什么需要进行forkjoin,因为只有对象和不是您需要调用的API)

this.logger.debug(`ConfigJson found for file '${element.name}': ${configJson}`);
const jsonContent = JSON.parse(configJson);
const entries = Object.entries(jsonContent);
entries.forEach((entry: any) => {
 // code
});

完整的代码应在类似的行上显示:-

getAssetfilesData(assetIds: Array<string>, filter: RegExp, showConfig: boolean): Observable<AssetFilesTableItem[][]> {
    const data = assetIds.map(assetId => {
      // for each assetId
      return this.fileService.getFileList(assetId)
        .pipe(
          map((datasets: any) => {

            // iterate over each element
            datasets.forEach((element: AssetFilesTableItem) => {

              return this.extractZipService.getJSONfromZip(assetId, element.name, 
              'config.json')
            });               
           })).map((configJson: any) => {
              // collect your results and return from here        
              // return result
         });;    
     });

    // return combined result of each assetId request
    return forkJoin(data);
  }
}

我创建了一个Stackblitz(https://stackblitz.com/edit/nested-subscribe-solution),它沿相同的方向工作。您需要使用concatMap和forkJoin来获取所有结果。 希望这会有所帮助。