带有 HttpClient 的 Angular 异步函数

Posted

技术标签:

【中文标题】带有 HttpClient 的 Angular 异步函数【英文标题】:Angular Asynchronous function with HttpClient 【发布时间】:2021-10-29 21:10:05 【问题描述】:

好的,所以我整天都在为这个问题摸不着头脑。本质上,我有一个按钮可以让学生退学。那部分很简单。但是,它还应该检查数据库中该班级的等候名单,如果有的话,请登记下一个排队的人。

所以在我看来,操作的顺序是……

    存储课程 ID 以供将来使用 让学生 A 退出课堂 (HTTP #1) 获取该课程的等候名单(这是我使用保存的 ID 的地方)(HTTP #2) 制作一个响应数组 如果数组长度为0,则结束 如果数组长度大于0,继续 从数组 [0] 创建一个学生 B 对象 让学生 B 加入课程 (HTTP #3) 从等候名单中删除学生 B。 (HTTP #4)

我可以很好地编写代码以进行编译,并且每个单独的部分都可以正常工作。但是在一起,我的同步函数变得异步并且不能按预期工作。罪魁祸首是第 3 步和第 4 步。虽然第 3 步仍处于等待状态,但 JS 移动到第 4 步并且代码停止,因为显然数组是空的。

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;
      )

      //do this 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")
        )
      
    
  

和 api 服务 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;
        ))
      

我认为使用 async/await 将是“正确”执行此操作的最佳方法,但我不知道如何从 http 调用中获得承诺或等待它。

(ps。如果你正在查看我的 httpClient 调用并说“你有 .put 用于删除和 .post 用于获取”我知道。这很奇怪。这只是让它工作的唯一方法Cosmos DB。它们都有效,我保证,这不是问题。)

已解决:

感谢 munleashed 的帮助!在我的情况下,他的回答并没有完全解决这个问题,但这就是让我头脑发热的原因。对于在这里寻找解决方案的任何人,这就是我的结尾......

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);
      );
    
  

【问题讨论】:

【参考方案1】:

我不确定我是否完全理解您想要做什么,但我发现您在 api 调用的同步方面遇到了问题。 如果您想等待一个 API 调用完成以便调用另一个 API 调用,则必须使用一些 RXJS 可管道运算符。 比如:

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

api1.pipe(switchMap(api1Result => 
// Here you can use result of api1 call

return ap2;
).subscribe(api2Result => 
// Here you can use result of api2Call
);

基本上,机制与 Promises 的工作方式类似:

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 at the end because its an asyns operation)

Observables 和 RXJS 的逻辑是一样的。这就是为什么您无法将代码与您的方法同步的原因。看看我的第一个例子,然后从它开始。

【讨论】:

感谢您的帮助,@munleashed!

以上是关于带有 HttpClient 的 Angular 异步函数的主要内容,如果未能解决你的问题,请参考以下文章

带有 .Get() 对 Azure 搜索 API 的请求的 Angular HttpClient“未知错误”

Angular 6 HTTPClient:请求触发一次,收到2个响应

带有json的Angular 5 [object Object]错误

为啥 Angular 将 Observable 用于 HttpClient?

Angular:如何扩展 HttpClient?

Angular 4 HttpClient 查询参数