Home > front end >  Best way to get value of RxJs Observable in a nested array of objects to resolve?
Best way to get value of RxJs Observable in a nested array of objects to resolve?

Time:11-13

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).

  • Related