Dealing With HttpClient and Asynchronous Functionality in Angular

I've been pondering this issue all day. I have a button that should withdraw a student from a class, which is straightforward. However, it should also check the database for a waiting list for that class and enroll the next person if there is any.

In my mind, the sequence of operations would be...

  1. store the course ID for future use
  2. withdraw Student A from the class (HTTP #1)
  3. retrieve the waiting list for that class using the saved ID (HTTP #2)
  4. create an array of the response
  5. IF the array length is 0, finish
  6. IF the array length is greater than 0, continue
  7. create a student B object from array[0]
  8. enroll student B into the class (HTTP #3)
  9. remove student B from the waiting list (HTTP #4)

I can write the code and compile it, and each individual part works fine. However, when combined, my synchronous function becomes asynchronous and fails to work as intended. The main issue lies with steps 3 and 4. While step 3 is pending, JS moves on to step 4, causing the code to stop because the array is empty.

onWithdrawClass(row:any){
    let courseId = row.courseId;

    //display a confirmation message
    if(confirm("Are you sure you want to withdraw from this course?\n \nYou may not be able to get your seat back if you do.")){
      
      //use the api service to perform the withdraw and refresh the list
      this.api.deleteWithdraw(row.id).subscribe(res=>{
        alert("You have been successfully withdrawn from this course.");
          let ref = document.getElementById('refresh');
          ref?.click();
      },
      err=>{
        alert("Something went wrong with withdrawal.\nTry refreshing the page to see if it worked.");
      })

      //get waiting list for that course
      this.api.getWaitlistByCourseId(courseId).subscribe(res=>{
        this.courseById = res;
      })

      //proceed if there is someone waiting for that class
      if(this.courseById.length > 0){
        //create student object
        this.studentObj.id = this.courseById[0].id;
        this.studentObj.courseId = this.courseById[0].courseId;
        this.studentObj.courseName = this.courseById[0].courseName;
        this.studentObj.courseInstructor = this.courseById[0].courseInstructor;
        this.studentObj.studentFirstName = this.courseById[0].studentFirstName;
        this.studentObj.studentLastName = this.courseById[0].studentLastName;
        this.studentObj.studentEmail = this.courseById[0].studentEmail;
        
        //enroll that student from wait list into course
        this.api.enrollStudent(this.studentObj).subscribe(res=>{
          console.log("waiting list student enrolled.")
        },
        err=>{
          console.log("error with enrolling waiting list student.")
        })
        //remove that student from wait list
        this.api.deleteFromWaitingList(this.courseById[0].id).subscribe(res=>{
          console.log("student removed from waiting list")
        },
        err=>{
          console.log("error with deleting student from waiting list")
        })
      }
    }
  }

and the api service ts

enrollStudent(data:any){
        return this.httpClient.post<any>("ENROLL-URL", data)
        .pipe(map((res:any)=>{
          return res;
        }))
      }

deleteWithdraw(id:number){
        return this.httpClient.put<any>("WITHDRAW-URL", id)
        .pipe(map((res:any)=>{
          return res;
        }))
      }

getWaitlistByCourseId(courseId:string){
        return this.httpClient.post<any>("WAITLIST-URL", courseId)
        .pipe(map((res:any)=>{
          return res;
        }))
      }

deleteFromWaitingList(id:number){
        return this.httpClient.put<any>("WAITLIST-DELETE-URL", id)
        .pipe(map((res:any)=>{
          return res;
        }))
      }

I thought using async/await would be the proper way to handle this, but I couldn't figure out how to get a promise from an http call or await it.

(ps. If you're looking at my httpClient calls and noticing the mismatch between .put for a delete and .post for a get, I'm aware. It's odd, but it's the only way to make it work with Cosmos DB. They all function correctly, so that's not the issue, I assure you.)

SOLVED:

Big thanks to munleashed for the assistance! While his solution didn't directly solve my issue, it got my thought process going. For those seeking solutions, here is what I ended up with...

courseById: any[] = [];

async doTheThingZhuLi(courseId: undefined){
    this.response = await this.http.post<any>("WAITLIST-URL", courseId)
    .pipe(map((res:any)=>{
      this.courseById = res;
    })).toPromise();
  }

onClickWithdraw(row:any){
    if(confirm("Are you sure you want to withdraw from this course?")){
      this.doTheThingZhuLi(row.courseId).then(res=>{
        this.moveFromWaitList(); //this handles steps 6 - 9
      }
      ).finally(() =>{
        this.actuallyWithdrawTheStudent(row.id);
      });
    }
  }

Answer №1

It seems like there may be some confusion about what exactly you're trying to achieve, but it appears that there is an issue with synchronizing the API calls. If you need to wait for one API call to finish before making another API call, you'll need to utilize some of the RXJS pipeable operators. Here's an example:

const api1 = this.service.getApi1();
const api2 = this.service.getApi2();

api1.pipe(switchMap(api1Result => {
// You can use the result of api1 call here

return api2;
}).subscribe(api2Result => {
// You can use the result of api2 call here
});

Essentially, the concept is similar to how Promises work:

console.log(1);
const promise = new Promise((res) => {res()});
promise.then(res => console.log('3'));
console.log(4);

// It will print 1 4 3 (3 comes last because it's an asynchronous operation)

The principle is the same with Observables and RXJS. This is why you can't synchronize your code with your current approach. Take a look at my first example and build from there.

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

When using form.serialize() in Django forms, an empty object is being sent

Upon clicking the button, my AJAX request is sending an empty object Object { } instead of form data. The form on my page consists of checkboxes and its HTML structure is as follows: <form method="post" action="" data-id="filter-form"> //Included ...

Utilizing the import feature for structuring the routes folder in Express with Node.js

Recently, I made the switch to using ECMAScript (ES6) for my NodeJS Rest API with Express, and I've encountered a few challenges when working with the new keyword import In the past, I would organize my routes folder like this: Let's imagine th ...

Struggling with establishing recognition of a factory within an Angular controller

I am currently facing an issue while trying to transfer data from one controller to another using a factory in Angular Seed. My problem lies in the fact that my factory is not being recognized in the project. Below is my snippet from app.js where I declare ...

What is the method for importing jQuery from a local source?

My experience with CDNs has been seamless, but I've run into some issues when trying to use jquery/jquery mobile locally. It seems that certain classes work intermittently, while others don't work at all. The versions I am using are: jQuery - ...

Issue with Vue.js: Nested field array is triggering refresh with inaccurate information

I've been working on a Vue page where I want to have nested field collections, with a parent form and repeatable child forms. Everything seems to be working fine except that when I try to delete one of the child forms, the template starts rendering i ...

transforming a text input into unadorned plain text

Currently, I am in the process of creating a small HTML form that consists of a single textbox input. Once the user enters text into this textbox and clicks on a button located at the end of the page, I would like the textbox to transform into normal plain ...

What is the best approach to extracting tightly-coupled code and converting it into an external library?

I have a question regarding paradigms that I would like to address, and if this is not the appropriate platform, please guide me to the right place. Your recommendations are most welcome :) Currently, I am tasked with extracting a significant piece of fun ...

How can I effectively test a method within a React component using Jest and Typescript?

When working on .tsx components using Typescript and React, I want to write unit tests for the methods within my React component. For example: export default class SomeComponent extends React.Component<undefined, SomeComponentState> { someMetho ...

Checkbox search column in jQuery DataTables

I have implemented jQuery DataTables Individual Column Searching on a table where one of the columns contains checkboxes. HTML Structure <table id="NewTable" class="table table-bordered table-striped"> <thead> <tr> ...

Adjust the size of the text within the div element as needed

Here is my code on jsFiddle where I am dynamically changing the text font to fit in the div. Everything is working perfectly fine. Currently, the text is within a span element, but I would like to remove the span and have the same functionality. However, w ...

Utilizing the HTML5 Download attribute for linking to external files

I am currently developing a web application for my own personal needs. One feature I would like to implement is the ability to set the download attribute on certain links. However, I have run into an issue where the files to be downloaded are hosted on ex ...

Performing a bulk update using the array provided as the base for the update statement

In my database, there is a table called sched that I frequently need to update. To facilitate this process, I have created an HTML interface that displays the current values of the sched table for users to edit. Upon making changes, scripts are triggered ...

Angular - Strategies for Handling Observables within a Component

I am new to Angular and recently started learning how to manage state centrally using ngRx. However, I am facing a challenge as I have never used an Observable before. In my code, I have a cart that holds an array of objects. My goal is to iterate over the ...

Can anyone suggest a node.js equivalent for XMLHttpRequest.readyState in http?

One important feature of Javascript's XMLHttpRequest object is the readyState property. This property, often used alongside response codes, allows developers to determine if an HTTP request has completed before proceeding with additional code. if (th ...

Guide to creating a React Hooks counter that relies on the functionality of both a start and stop button

I am looking to create a counter that starts incrementing when the start button is clicked and stops when the stop button is pressed. Additionally, I want the counter to reset to 1 when it reaches a certain value, for example 10. I have tried using setInte ...

Chrome's rendering of CSS flex display shows variations with identical flex items

I am facing an issue with my flex items where some of them are displaying incorrectly in Chrome but fine in Firefox. The problem seems to be with the margin-right of the rectangle span within each flex item, along with the scaling of the text span due to f ...

Looking to create a clone of an HTML element, make some modifications, and extract the HTML string without impacting the DOM?

After working with some HTML code, I encountered the following: <div style="background:red;width:900px;height:200px" id="wrap"> <div id="inner" style="width:300px;float:left"> ... </div> </div> The tasks at hand are ...

Creating a Copy to Clipboard feature in React can be achieved by transferring data from an h1 tag to an input field

I'm trying to extract data from an API into an h1 tag in my app. Is there a way for me to easily copy this data and paste it into an input field? Any help would be greatly appreciated. Thanks! ...

Vue.js Contact Form Issue: Error message - 'Trying to access 'post' property of an undefined object'

Currently, I am encountering the error 'cannot read property 'post' of undefined' in my code, but pinpointing the exact mistake is proving to be a challenge. Given that I am relatively new to Vue JS, I would greatly appreciate it if som ...

Utilizing the $.ajax method along with the onreadystatechange event

Is there a way to use the onreadystatechange event of the underlying XMLHttpRequest in JQuery's (version 2.0.2) $.ajax(...) function to trigger synchronous ajax requests for displaying accurate status indications during long-running processes? It seem ...