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...
- store the course ID for future use
- withdraw Student A from the class (HTTP #1)
- retrieve the waiting list for that class using the saved ID (HTTP #2)
- create an array of the response
- IF the array length is 0, finish
- IF the array length is greater than 0, continue
- create a student B object from array[0]
- enroll student B into the class (HTTP #3)
- 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);
});
}
}