user7332973
user7332973

Reputation:

Chaining observables with flatMap

I'm working with observables and the flatMap operator, I wrote a method which makes and API call and returns an observable with an array of objects.

Basically what I need is to get that array of objects and process each object, after all items are processed. I want to chain the result to make an extra API call with another method that I wrote.

The following code does what I need:

  this.apiService.getInformation('api-query', null).first().flatMap((apiData) => {
    return apiData;
  }).subscribe((dataObject) => {
    this.processService.processFirstCall(dataObject);
  }, null, () => {
    this.apiService.getInformation('another-query', null).first().subscribe((anotherQueryData) => {
      this.processService.processSecondCall(anotherQueryData);
    });
  });

But this approach isn't optimal from my perspective, I would like to do chain those calls using flatMap but if I do the following:

   this.apiService.getInformation('api-query', null).first().flatMap((apiData) => {
    return apiData;
  }).flatMap((dataObject) => {
    this.processService.processFirstCall(dataObject);
    return [dataObject];
  }).flatMap((value) => {
    return this.apiService.getInformation('another-api-query', null).first();
  }).subscribe((value) => {
    this.processService.processSecondCall(value);
  });

The second API call executes once for each item on the apiData array of objects. I know I'm missing or misunderstanding something. But from the second answer of this thread Why do we need to use flatMap?, I think that the second flatMap should return the processed apiData, instead is returning each of the object items on that Array. I would appreciate the help.

Thank you.

Upvotes: 2

Views: 6066

Answers (2)

CozyAzure
CozyAzure

Reputation: 8478

What you want is the .do() operator, and not flatMap(). flatMap() is going to transform an event to another event, and in essence, chaining them. .do() just executes whatever you instruct it to do, to every emission in the events.

From your code, there are 2 asynchronous methods (calls to the api) , and 2 synchronous (processService). What you want to do is :

  1. Call to the first API (async), wait for the results
  2. Process the results (sync)
  3. Call to the second API (async), wait for the results to come back
  4. Process the results (sync)

Hence your code should be :

this.apiService.getInformation('api-query', null)//step1
    .first()
    .do((dataObject) => this.processFirstCall(dataObject))//step2
    .flatMap(() => this.apiService.getInformation('another-api-query', null))//step3
    .first()
    .do(value => this.processService.processSecondCall(value))//step4
    .subscribe((value) => {
        console.log(value);
    });

I wrote in comment the steps corresponding to the list above. I also cleaned up unnecessary operators (like your first flatMap is kinda redundant).

Also, in the event you want to transform your data, then you should use .map() instead of .do(). And I think that is the place where you are confused with .map() and .flatMap().

Upvotes: 5

SimplyComplexable
SimplyComplexable

Reputation: 1114

The issue I think your encountering is that flatmap should be applied to an observable or promise. in your second code example, you are returning data within the flatmap operator which is then passed to the following flatmap functions, whereas these should be returning observables. For example:

this.apiService.getInformation('api-query', null).first()
  .flatMap((dataObject) => {
    return this.processService.processFirstCall(dataObject);
  }).flatMap((value) => {
    return this.apiService.getInformation('another-api-query', null)
  }).subscribe((value) => {
    this.processService.processSecondCall(value);
  });

See this post for futher clarification.

Upvotes: 0

Related Questions