Guide to implementing nested asynchronous calls using a foreach loop

I am eager to enhance my skills and gain a better understanding of how to improve this code. While the current code is functioning, I suspect there may be an issue that needs addressing. Here's the concept:

1: Initially, I send the first HTTP request to the API and retrieve the data.

2: The key component here is the matchHistoryArr containing an array of 10 "gameIds." I need to iterate through these gameIds using a forEach loop to call this.summ.getMatches(gameId) for each gameId. The retrieved data must be stored in a new local array for comparison with another array since async operations do not provide sorted results by default. After much research and trial, I arrived at the solution presented within the code (marked for reference). Note: This section posed a significant challenge for me.

3: Next, I plan to fetch additional data from other endpoints; however, I feel it's necessary to reorganize the code structure before proceeding.

Component.ts

     onSubmit(){
** 1 **
    this.summ.getSummoner(this.summoner.name,this.summoner.regionId)
    .subscribe(  (data:any) => {

      let matchHistoryArr = data.matchHistory

      let gameIdArray = matchHistoryArr.map( element => element.gameId)

      const subscribers: Subscriber<any>[] = [];
        const promiseMatchesArray = [];

** 2 **
       matchHistoryArr.forEach( element => {

         promiseMatchesArray.push( this.summ.getMatches(element.gameId).toPromise().then( match => {

           subscribers.push(match)
         }));
         });

           Promise.all(promiseMatchesArray).then( a =>{
               if ( subscribers.length === matchHistoryArr.length ){

                let arraySort = [];
                arraySort = subscribers
                let dataParticipants

                  let matchesArraySorted = arraySort.sort((a, b) => {  
                   return gameIdArray.indexOf(a.gameId) - gameIdArray.indexOf(b.gameId);
                 });

                 this.matchesArray = matchesArraySorted

                 matchesArraySorted.forEach( x => {

                  dataParticipants = x.participants
                  });

                  if ( dataParticipants.length === 10 ){

                    this.dataParticipants = dataParticipants
                  }
               }
           });


      this.matchHistory = matchHistoryArr
      this.SummInfo = data.summonerdata;

     });

  }

Services.ts

export class SummonerService {

  constructor( private http:HttpClient ) { }

  summonerName

    getSummoner(summonerName,regionId){
    this.summonerName = summonerName
   return this.http.get(`http://localhost:3000/summName/${summonerName}/${regionId}` )
   .pipe(
    map( (data:any) => data)
   )};



   getChampImage(champId){
     return this.http.get(`https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-icons/${champId}.png`)
   };

   getMatches(gameId){
     return this.http.get( `http://localhost:3000/match/${gameId}` )
     .pipe( 
       map( (data:any) => this.uploadData(data))
       )};

Answer №1

Essentially, your scenario can be simplified into a two-step process:

  1. Retrieve data from an observable
  2. Perform a series of actions based on the data obtained in step 1.

I suggest maintaining observables throughout unless there is a strong justification for utilizing promises.

  1. Initiate the initial call as you are currently doing 1a. Transition to a new observable using concatMap
  2. Generate an array of observables and execute them simultaneously using forkJoin
onSubmit() {
  this.summ.getSummoner(this.summoner.name, this.summoner.regionId)
    .pipe(
      tap((summoner) => this.matchHistory = summoner.matchHistory),
      concatMap((summoner: any) => {
        const observables = this.matchHistory
          .map(element => this.summ.getMatches(element.gameId));
        return forkJoin(observables)
      }),
      map(matchesArray => {
        // TODO: perform sorting
        return matchesArray;
      })
    ).subscribe(matchesArray => {

    });
}

I have excluded the sorting process to keep the explanation straightforward.

matchesArray represents an array containing the outcomes that correspond to the input observables.

To implement any sorting requirements, you can utilize the map operator.

If you need to preserve any state outside of the pipe, you can achieve this with a tap operator.

The key point to remember when combining observables is that there is only one primary subscriber. Observables can be merged in various ways through pipe operators, making them more potent than promises in intricate scenarios.

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

What is the best way to enclose all succeeding elements with an HTML tag after a specific element?

In my project, I am integrating Vue 2 with Sanity.io and I am looking for a solution to wrap all elements that come after a specific element with an HTML tag, and then wrap this element along with the following elements with another HTML tag. For instance ...

Updating a particular element within an array in Firestore

Hello everyone, I'm currently working with Google Firestore and Angular. I am facing a challenge where I need to update a particular element within an array. I have experimented with various solutions but unfortunately, none of them have been success ...

Having trouble preventing Selenium webdriver from automatically closing the safari browser

Experiencing a strange issue with Safari where it closes the browser immediately after running the code snippet below, while Chrome and Firefox behave as expected. I am using Webdriver with JavaScript for this automation. let driver = await new Build ...

Taking a Breather with mywindow.print()

I'm currently utilizing a fantastic printing script that I found: <script type="text/javascript"> function PrintElem(elem) { Popup($(elem).text()); } function Popup(data) { var mywindow = window.ope ...

Are there any compatibility issues with uuid v1 and web browsers?

After researching, I discovered that uuid version1 is created using a combination of timestamp and MAC address. Will there be any issues with browser compatibility? For instance, certain browsers may not have access to the MAC address. In my current javaS ...

How can I determine the quantity of pairs in an array using JavaScript?

My current approach is not providing me with the desired result. I am facing an issue where pairs of 1 are being counted multiple times, which is incorrect. What I actually need is to count only the "perfect" pairs, such as the pair between 5 and 2 in this ...

Issue with "Access-Control-Allow-Origin" header missing in express and react application

I've decided to work on a Node/Express project to improve my JavaScript skills, but I'm facing an issue with 'Access-Control-Allow-Origin'. For some reason, I can't do a get request from the front end and despite looking at other p ...

Updating tooltip text for checkbox dynamically in Angular 6

Can anyone help me with this code? I am trying to display different text in a tooltip based on whether a checkbox is active or not. For example, I want it to show "active" when the checkbox is active and "disactive" when it's inactive. Any suggestions ...

Sending Text Input Data from Angular Form to a Restful API

I'm currently working on a project where I need to send data from a reactive form to a REST API running on my local express.js server. The form consists of basic text input fields like name, surname, and email. When the form data is submitted, it is ...

Storing JSON data in LocalStorage or within the App on Ionic 2

I am currently in the process of developing a mobile app for both IOS and Android platforms. The app will feature a list of objects including images, names, etc., which are stored on a backend server powered by node.js. My goal is to allow users of the ap ...

What is the process of choosing a language (English/French) within a pop-up?

<body style="text-align:center"> <h2>Popup</h2> <div class="popup" onclick="myFunction()">Interact with me to show/hide the popup! <span class="popuptext" id="myPopup">A Simple Popup!</span> </div> <script& ...

What is the best way to verify a field against a set of string values using Typescript in a NestJS application

I currently have an Enum containing various timezones listed below export enum Timezones { 'Europe/Andorra', 'Asia/Dubai', 'Asia/Kabul', 'America/Antigua' } In the DTO file, I am attempting to valid ...

Picking and modifying a newly added HTML input element using Jquery

I am encountering a problem that I haven't been able to find a solution for through research or Google. I managed to successfully add multiple input boxes to my HTML/JQuery project, which are displaying correctly on the webpage. for (i = 0; i < ma ...

How can I fetch data from SQL using JavaScript based on a specific value in PHP?

My application is built using the Yii2 framework. Within my application, there is a view.php file that consists of an element and a button. The element, <div id="userId">, contains the user's login ID, and I aim to use the button to re ...

Determining the visible dimensions of an iframe

Is there a way to determine the visual size inside an iframe using JavaScript or jQuery? In this scenario, only a portion of the iframe is displayed. The iframe itself has dimensions of 300x300, but it is being limited by a div to 300x100. <div style= ...

Tips for dynamically coloring table cells in Spotfire based on their values

Creating Dynamic Table with HTML After successfully creating a cross table in Spotfire, I now aim to replicate the same table in HTML within a text area. I managed to add values using calculated values, but I'm stuck on how to dynamically set the bac ...

Bring in a template from a URL using Angular and then compile it within a div

Being a beginner in Angular JS, I am curious about how to load an external template and compile it with specific data into a targeted div. Here is the sample template: <script type="text/ng-template"> <img src="{{Thumb}}" /> <script& ...

Ensuring Code Coverage with Husky in Angular 8 Pre-Commit Hooks

I am currently working on an angular 8 project and I want to implement a rule where the user can only commit if a certain percentage of code coverage passes. Currently, I am using NX Workspace and Husky to run Linting before committing. In addition to thi ...

Just starting out with jQuery: seeking advice on a user-friendly slideshow plugin, any tips on troubleshooting?

I am currently trying to incorporate a basic jquery slideshow plugin, but I seem to be encountering some difficulties. The documentation mentions the need to install 'grunt' and 'node dependencies', which is confusing to me as I am new ...

I find myself with just one button in my javascript function, but I actually need two

I'm currently working on a class assignment where I'm trying to create a javascript function that uses a button to display the value on a slider component. However, I'm running into an issue where only one button appears instead of two, even ...