Reputation: 469
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
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
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
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
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
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