Angular 如何在 subscribe 中使用 await

40

我是一个关于Angular应用程序的新手。我不太理解subscribe的工作原理。我目前的障碍是我不明白为什么console.log("B")console.log("A")之前执行,因此呈现出空数组的结果(请参见console输出的链接)。

我尝试将所有代码放入使用async/await等待函数的函数中。我不明白为什么它不起作用。

在这种情况下,等待订阅的最佳方法是什么?

console.log("B")必须在console.log("A")之后执行。

this._roleService.getRoleTypes(this.token).subscribe(
    response => {
        if(response.status != "error" && response.code != 400){
            let _roleTypes:Array<RoleType> = new Array<RoleType>(); 
            _roleTypes = new Array<RoleType>();
            response.data.forEach(rt => {
                let roleType:RoleType = new RoleType(
                    rt.id,
                    rt.name
                );
                _roleTypes.push(roleType);
            });
            console.log("A");
            console.log(_roleTypes);
            this.roleTypes = _roleTypes;
        }
        else{
            this._loginService.destroySession();
        }
    },error => {
        this.errorMessage = <any>error;
        if(this.errorMessage != null){
            console.log(this.errorMessage);
            alert("Petition Error");
        }
    }
);
console.log("B");
console.log(this.roleTypes);
5个回答

55

您可以使用firstValueFrom方法来实现这一点。这也是处理异步操作的好方法。只需删除订阅并添加此方法,然后在调用此方法的方法中添加async关键字。

例如-

 const response = await firstValueFrom(this._roleService.getRoleTypes(this.token));

现在,您将首先获得console.log("A"),然后是console.log("B")。

更新: firstValueFrom 仅适用于 RxJS v7 及以上版本。如果您使用的是 RxJS v6 或更低版本,则需要使用 .ToPromise()。所以:

 const response = await this._roleService.getRoleTypes(this.token).ToPromise();

3
这是一个很好的答案。我会给它投双倍的赞同票。我很惊讶没有人将其标记为答案。这绝对值得订阅。 - Maverik
奇怪,但对我不起作用 :( - Jaydeep Suryawanshi
使用了 "@angular/core": "13.3.0""rxjs": "7.4.0" - JRichardsz

29

如果您需要同步执行代码,应使用async/await。将订阅代码包装在返回promise的函数中。

async getRolestypes(): Promise<Array<RoleType>> {

   return new Promise((resolve, reject) => {

     this._roleService.getRoleTypes(this.token).subscribe(
       response => {
         if(response.status != "error" && response.code != 400){
          let _roleTypes:Array<RoleType> = new Array<RoleType>(); 
          _roleTypes = new Array<RoleType>();
          response.data.forEach(rt => {
            let roleType:RoleType = new RoleType(
                rt.id,
                rt.name
            );
            _roleTypes.push(roleType);
           });
          console.log("A");
          console.log(_roleTypes);
          resolve(_roleTypes);
       }
       else{
           this._loginService.destroySession();
           reject('not a good response')
       }
       },error => {
        this.errorMessage = <any>error;
        if(this.errorMessage != null){
           reject(this.errorMessage);
        }
      }
    );


  })


}


this.roleTypes = await getRolestypes();
console.log(this.roleTypes);

8

如您所知,订阅用于处理异步方法调用。因此,在异步方法返回其结果(例如进行http调用后),仅执行subscribe()方法内的代码。

在等待异步响应时,程序继续执行并执行以下代码。这就是异步调用的目的!

这就是为什么console.log('B')会在console.log('A')之前执行的原因。

以下是示例:

this.myservice.asyncCall().subscribe( (result) => {
   // wait for the async response
   console.log('I get some data from asyncCall()');
});
// sync code executed, even if the previous async call doesn't respond anything
console.log('Code executed after the subscription. But not waiting for it to respond');

如果想要在订阅函数中输出console.log('B'),你需要把它移到else语句后面。根据你的目的,你也可以在那个位置调用一个方法。
你还可以查看.map()方法。这使你可以在订阅方法处理之前编辑所获取的响应(添加一些数据,转换它,记录或其他任何操作)。

抱歉,请问最佳的处理多个非异步操作的方式是什么? - Jon Doe
“多个非异步操作”是什么意思?每一行代码都是非异步操作。只有一些需要在末尾添加“subscribe”的方法才是异步的。 - Kapcash
1
可以使用await吗? - Lucas Selliach
1
Await旨在模拟对异步方法的同步行为。在继续下一行代码之前,您的代码将等待承诺完成。因此,在这里目的不同!但是,是的,ES6 await/async可以与Angular 2+承诺一起使用。对于AngularJS,请查看$q。 - Kapcash

5

@sunny的回答是我首选的选择。但是toPromise在RxJs 7已被弃用,并且在发布RxJs 8时可能不再存在。因此,新的方法将是:

import { lastValueFrom } from 'rxjs';

const roleTypes$ = this._roleService.getRoleTypes(this.token);
const response = await lastValueFrom(roleTypes$);

-1

subscribe是异步执行的。在您的情况下,subscribe将被执行,然后console.log('B')将在此之后执行。稍后,当Observable发布响应(基于成功/错误)时,相应的成功/错误回调将被调用。因此,您可以在成功回调中处理console.log('B')


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接