我知道在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和此处建议的解决方案,但是没有运气。我确信有可能使它以某种方式工作,但我的想法已经用完了。任何帮助,将不胜感激!
答案 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来获取所有结果。 希望这会有所帮助。