2

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 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
                };
              })
        }));
  }
1
  • I'd use switchMap (instead of map) in conjunction with forkJoin (returned result of switchMap). The idea here is to wait for all those importantInfoCall invocations (I'm assuming these are async in nature) to complete then do what you need to with the result of every invocation. You may want to pair each invocation result with the relevant file it pertains to as well for context. Commented Nov 12, 2021 at 3:54

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.