Jon Doe
Jon Doe

Reputation: 469

angular how to await subscribe

I am newbie regarding Angular applications. I don't understand exactly how subscribe works. My current blocker is that I don't understand why the console.log("B") is executed before the console.log("A"), consequently presenting the result of an empty array (see the links to console output).

I tried to put all the code in a function with async/await to wait for the function. I don't understand why it doesn't work.

What is the best way in this case to wait for a subscription?

The console.log("B") must be executed after the 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);

Upvotes: 44

Views: 134599

Answers (5)

Sunny
Sunny

Reputation: 1476

you can achieve this using the firstValueFrom method. It is also a great way to handle asynchronous operations. Just remove the subscribe and add this method and add the async keyword in the method from where you are calling this method.

Example-

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

So, Now you will get console.log("A") first then console.log("B").

UPDATE: firstValueFrom is only available from RxJS v7 onwards. If you are using RxJS v6 or less, you will need to use .ToPromise(). So:

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

Upvotes: 60

Salem Ouerdani
Salem Ouerdani

Reputation: 7916

@sunny answer above is my preferred choice. But toPromise has been deprecated in RxJs 7 and won't probably be there when RxJs 8 is released. So the new way would be:

import { lastValueFrom } from 'rxjs';

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

Upvotes: 5

Charlie
Charlie

Reputation: 23858

You should use async/await if you need synchronous execution of your code. Wrap your subscribe code in a function that returns a 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);

Upvotes: 30

Kapcash
Kapcash

Reputation: 6919

As you may know, subscriptions are used to handle async method call. Thus, the code inside the subscribe() method is executed only when the async method return its result (after a http call for instance).

While waiting for the async response, the program continues and execute the following code. That's the goal of async calls!

That's why your console.log('B') is executed before your console.log('A').

Here is an example:

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

If you want you're console.log('B'), you have to move it into your subscription function (after the else statement). You can also call a method from that location, depending on the purpose you're looking for.

You may take a look at the .map() method as well. This allows you to edit the retrieved response before you handle it in the subscribe method (to add it some data, transform it, log or anything).

Upvotes: 9

Furqan Shaikh
Furqan Shaikh

Reputation: 381

subscribe is executed asynchronously. In your case, subscribe will execute and then console.log('B') will get executed after that. When at a later point of time, the Observable posts the response (based on success/error), corresponding success/error callback will be invoked. so you can handle console.log('B') in in the success callback

Upvotes: -1

Related Questions