javascript - Angular 订阅内订阅 : data doesn't load at the same time within view

标签 javascript angular typescript rxjs

我知道在订阅中调用订阅是不好的做法,但我不知道如何针对我的特殊情况进行不同的处理。 现在的代码可以工作,但我的问题是,如果我每秒更新我的网站,则首先加载表的部分内容,然后加载其他部分(我订阅中的订阅内容)。

我有一个服务,其中包含一个函数,该函数返回不同 Assets 的文件列表的可观察对象。 在该函数中,我通过调用另一个服务来请求每个 Assets 的文件列表,并且该服务返回可观察值。 然后,我迭代该列表的元素并构建我的数据结构以便稍后返回它们(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 ,它应该结合我对不同 Assets 的所有 fileList 请求。我不明白为什么我的 View 没有立即加载。

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

最佳答案

您正在 for 循环内调用 this.extractZipService.getJSON。因此,此方法被称为异步,并且您的 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 来获取所有结果。 希望这会有所帮助。

关于javascript - Angular 订阅内订阅 : data doesn't load at the same time within view,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57306755/

相关文章:

Javascript 鼠标滚动增量

angular - Angular 2在ReactJS中是否有一个 'equivalent'到componentDidMount

javascript - Angular 无法读取未定义的属性 'navigate'

javascript - 计算和删除嵌套数组中的多个元素

javascript - Babylon.js OnIntersectionEnterTrigger 未使用相机触发

typescript - 如何在 typescript 中使用 faker.js?

javascript - 将 html 从变量解析为 jQuery 对象

javascript - 组织 ExtJS 项目的最佳方式

javascript - 使用Typescript的Google登录网站和Angular 2

angular - 当一个库依赖于 angular6 工作区中的另一个库时构建失败