Waiting for an Observable to complete in an Angular map operation

Attempting to send HttpClient in an Observable function without waiting for it to complete.

Below is a sample code snippet to replicate the issue:

    test() {
    this.test2()
      .pipe(
        mergeMap((result) => {
          console.log(result[0]);
          return of([]);
        })
      )
      .subscribe();
  }
  test1(): Observable<any> {
    return of(this.getStudents());
  }
  test2(): Observable<any> {
    return this.test1().pipe(
      mergeMap((result) => {
        if (result) {
          result.map((rr) => {
            if (rr.age == 1) {
              this.client
                .get('https://api.coindesk.com/v1/bpi/currentprice.json')
                .subscribe((res) => {
                  console.log(res);
                  rr.age = rr.age * 10000;
                });
            } else {
              rr.age = rr.age * 10;
            }
            return rr;
          });
        }
        return of(result);
      })
    );
  }
  getStudents(): Student[] {
    return [{ age: 1 }, { age: 2 }, { age: 3 }];
  }

This is Student

export class Student {
  age: number;
}

To achieve the expected result, console.log(res); should come before console.log(result[0]);.

Tried methods like .toPromise and async await, but didn't succeed in resolving the issue.

You can try out a modified version here:

https://stackblitz.com/edit/save-file-to-drive-7dawzd?file=src/app/app.component.ts

Answer №1

As per your desired outcome, it is crucial for the API call to be completed before displaying Student[0].

The issues with your code are:

  • You are subscribing to an API call without waiting for it to finish, leading to console.log(result[0]) being executed before console.log(res); because the API call is still in progress.

I employed various RXJS operators to achieve the desired result.

  • mergeMap was used to flatten the nested observable.
  • of aided in converting the student array into an observable.
  • map assisted in transforming the current array into a new array with updated ages.
  • forkJoin consolidated multiple requests into one observable and only concludes once responses from all requests are received.

This approach is just one way of accomplishing this task, and there could be more efficient methods available.

test() {
  this.test2()
    .pipe(
      mergeMap((result) => {
        console.log(result);
        if (result && result.length > 0) {
          console.log(result[0]);
        }
        return of([]);
      })
    )
    .subscribe();
}
test1(): Observable < Student[] > {
  // return of(null);
  return of(this.getStudents());
}
test2(): Observable < Student[] > {
  return this.test1().pipe(
    mergeMap((result) => {
      if (!result) {
        return of(result);
      }
      return forkJoin(
        result.map((rr) =>
          rr.age === 1 ?
          this.client
          .get('https://api.coindesk.com/v1/bpi/currentprice.json')
          .pipe(
            map((res) => {
              console.log(res);
              return (rr.age = rr.age * 10000);
            })
          ) :
          of ((rr.age = rr.age * 10))
        )
      ).pipe(
        map((paths) => {
          return result.map((e, index) => ({
            age: paths[index],
          }));
        })
      );
    })
  );
}

getStudents(): Student[] {
  return [{
    age: 1
  }, {
    age: 2
  }, {
    age: 3
  }];
}

I made modifications to the Stackblitz you set up to simulate the solution.

Answer №2

The RxJS Approach:

When dealing with API endpoint responses that are not immediately needed, leveraging RxJS higher-order operators like mergeMap, switchMap, or concatMap allows you to manage waiting for the results efficiently.

For processing an entire array of observables simultaneously, forkJoin stands out as a convenient option. The forkJoin method will collect the last values from each observable and emit them in an array once they have completed.

test() {
  // Removing mergeMap as it doesn't serve any purpose here.
  this.test2().subscribe(
    students => console.log(students[0])
  );
}

test1(): Observable<Student[]> {
  return of(this.getStudents());
}

test2(): Observable<Student[]> {

  return this.test1().pipe(

    map(students => students?.map(student => {
      if(student.age == 1){
        return this.client.get(
          'https://api.coindesk.com/v1/bpi/currentprice.json'
        ).pipe(
          tap(currentPrice => console.log(currentPrice)),
          mapTo({...student, age: student.age * 1000})
        )
      }else{
        return of({...student, age: student.age * 10})
      }
    })),

    mergeMap(studentCalls => forkJoin(studentCalls))

  );
}

getStudents(): Student[] {
  return [{ age: 1 }, { age: 2 }, { age: 3 }];
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Adjust the placement of a div within another div based on various screen sizes dynamically

Currently, I am working on an Ionic 2 app where the user is required to select specific points on the screen. These coordinates will then be utilized on another screen with a different size. My initial attempt at using rule of three/cross multiplication pr ...

Can PassportLocalDocument and PaginateModel coexist within the same framework?

I am new to TypeScript and NestJS, looking to implement a pagination feature for all models in my application. Currently using NestJS with Mongoose for the API project. Here is an example of the user schema: export const UserSchema = new mongoose.Schema( ...

Determine the class of an object within the "keyof" parameter by utilizing both property and generic types

I have a requirement to create an interface with generic types that can accept an object with keys representing "root field names" and values as arrays of objects defining sub-fields with the key as the name of the sub-field and the type as the value' ...

Encountering difficulties while attempting to transition from angular 9 to angular 10

I attempted to upgrade my Angular project by running the following commands: $ ng update @angular/core@9 @angular/cli@9 $ ng update @angular/core @angular/cli However, when I executed the last command in the console, it resulted in an error message: Your ...

The React component is not refreshed reactively with Redux Observable, rxjs, and rx-http-request

I'm currently working with an API endpoint that provides a list of users in an 'application/stream+json' format where each item is separated by a new line character. If you want to see some example data, you can check it out here. Custom Co ...

Modify the content of a separate division by selecting a different item in a list with the help of Vue.js and TypeScript

I am still learning Vue and may not have all the answers. Currently, I am working on a feature that changes the text of another div based on the item I select from a list. You can find the code sandbox link below along with my current implementation. Code ...

Making an HTTP request within a forEach function in Angular2

Encountering an issue while using the forEach function with HTTP requests. The _watchlistElements variable contains the following data: [{"xid":"DP_049908","name":"t10"},{"xid":"DP_928829","name":"t13"},{"xid":"DP_588690","name":"t14"},{"xid":"DP_891890" ...

Is there a way to utilize req.query, req.params, or req.* beyond its original scope without the need to store it in a database?

Looking to streamline my code and apply the DRY pattern, I've been working on creating a helper function for my express http methods. The structure of each method is similar, but the req.params format varies between them. Here's how I attempted t ...

Anticipating the outcome of a function in asynchronous JavaScript

After taking a break from web development for a couple of years, I recently dove back into it. While I dabbled in AngularJS before, I have now shifted my focus to Angular2. My current challenge revolves around handling asynchronous JavaScript. Despite enc ...

Creating the upcoming application without @react-google-maps/api is simply not possible

After incorporating a map from the documentation into my component, everything seemed to be functioning correctly in the development version. However, when attempting to build the project, an error arose: Type error: 'GoogleMap' cannot be used as ...

Using the tensorflow library with vite

Greetings and apologies for any inconvenience caused by my relatively trivial inquiries. I am currently navigating the introductory stages of delving into front-end development. Presently, I have initiated a hello-world vite app, which came to life throug ...

What is the clarification on Angular's paramMap.getAll method?

Angular 4 introduced support for paramMap along with get and getAll methods : I comprehend this code snippet that retrieves the value of "id" route.paramMap.subscribe( params => this.productID = params.get('id') ); However, I am c ...

Problem with the Auto-fill Feature in PrimeNG Module

Check out my code on Gist:   https://gist.github.com/rickymuvel/8ddc4d14d90877329447ddde9c0aa835 The issue I'm facing involves the Autocomplete module in PrimeNG. It seems that the specific path in the ubigeo.service.ts file is not being called. Her ...

Verify if the Observable (HTTP request) has provided a response, and if not, hold for it?

In my Angular app, I have a calendarComponent that fetches entries from a MongoDB through firebase cloud functions using the calendarService. When creating a new entry, I display an addEventComponent dialog which requires data from the database to function ...

A step-by-step guide on incorporating a .env file into a React JS project that is written with TypeScript

I'm currently working on a React JS project with TypeScript. I know that we can utilize a .env file to define some configurations, like so: .env file REACT_APP_SOME_CONFIGURATION = "some value" We can then use these configurations in our c ...

I am having difficulty accessing the values stored in my cardTiles variable

I am facing an issue with a variable called cardTiles in my Angular 9 component. The variable is defined as cardTitles:Product[] = []; The Product class is defined as follows export class Product{ productName: string;} When I console.log(this.cardTi ...

Terminating a function during execution in JavaScript/TypeScript

Currently, I am in the process of developing a VSCODE extension using TypeScript. Within this extension, there is a particularly large function that is frequently called, but only the final call holds significance. As a solution, I am attempting to elimina ...

Angular 8: How to Retrieve Query Parameters from Request URL

Can I retrieve the GET URL Query String Parameters from a specific URL using my Angular Service? For example, let's say I have a URL = "http:localhost/?id=123&name=abc"; or URL = ""; // in my service.ts public myFunction(): Observale<any> ...

Press on a list item within Angular Material 2/4

My goal is to trigger a function when clicking on any row of a list or table. I have not been able to achieve this, so now I am attempting to have the function called when clicking on the first column of each row. The structure of my component.html, which ...

Encountered a problem while executing the command (npm install -g @angular/cli) on a Mac

Encountering issues while trying to set up angular/cli on my system. When attempting to run the command npm install -g @angular/cli in the terminal, I encountered error messages. Even using sudo as a prefix did not yield positive results. npm ERR! Error ...