Hi I am new to RxJs and observables. I am currently making a request that returns data for some files and a user. For each file I want to return an object with data in it but one of the keys is dependent on another request. How can I make this request so that the getData function waits until the inner observable resolves to a value?
function getData() {
return forkJoin([
filesApiRequest),
userApiRquest
])
.pipe(map(([files, userInfo]) => {
return files.getFilesList()
.map((file) => {
const name = file.getName();
const importantInfo = importantInfoCall(userInfo.name, name); // returns an observable.
// How can I get the value from this before returning? I need this piece of data for each
// file in the files list.
return {
data1,
data2,
name,
...,
hasImportantInfo: importantInfo
};
})
}));
}
CodePudding user response:
You need to use a "Higher order mapping operator" (switchMap
) which will subscribe to your inner observable source and emit the data you need. In your case, you need to make many calls (one for each file), so you can map your file list to an array of observables that emit the desired data. You can use forkJoin
again to wrap all these calls into a single observable:
function getData() {
return forkJoin([filesApiRequest, userApiRquest]).pipe(
switchMap(([files, userInfo]) => forkJoin(
// return array of observables
files.getFilesList().map(f => toDataWithImportantInfo$(f, userInfo))
))
);
}
function toDataWithImportantInfo$(file, userInfo) {
const name = file.getname();
return importantInfoCall(userInfo.name, name).pipe(
map(importantInfo => ({
data1,
data2,
name,
...,
hasImportantInfo: importantInfo
})
);
}
forkJoin
isn't the best solution when you have a large number of requests, because it will execute them all at the same time (maybe you don't want to kick off 500 http requests at once).
In order to limit the number of requests, we can use merge
instead, since it provides a concurrency parameter:
function getData() {
return forkJoin([filesApiRequest, userApiRquest]).pipe(
switchMap(([files, userInfo]) => merge(
files.getFilesList().map(f => toDataWithImportantInfo$(f, userInfo))
, 5)), // MAX 5 CONCURRENT
toArray()
);
}
forkJoin
emits an array of all results when all sources complete. merge
emits each result individually, so toArray
is needed if you want to emit a single array once all the inner sources complete (rather than emitting individually).