Reputation: 31
So my problem is hopefully relatively simple to solve. So (Angular 8 btw) I call a method and inside of that method I subscribe to an api call. Everything works except the return always is called a second or two later after the method and the return type is never returned properly. I tried MAP but that doesn't work properly. Let me code out a quick example:
return this.callApi( apiObject ); // I want to wait for subscribe and return data here
callApi( apiObject ) {
this.myApiService.someRandomPostApiCall( apiObject ).subscribe(res => {
console.log(res);
res.valid ? { return true } : { return false };
});
}
Upvotes: 0
Views: 9708
Reputation: 7360
The real answer is that you can't.
Let me rephrase what you want: you would like to have a method that performs a long server call to execute synchronously. If it would be possible, it would block the whole application for the 1 second or so needed to get the results. The whole application, browser rendering included.
Instead, in order to make it work, you must embrace asynchronicity. Unfortunately, this means working on the code that is consuming the result of the callApi method, which is not provided.
If you can provide it, we can reason on the best way to do the job.
Let me explain you what's happening in your code, numbering the rows by _execution time:
// I'm splitting the rows, just to better describe the order of execution
const somethingThatYouDonTExpect = this.callApi(apiObject); // this runs first
return somethingThatYouDonTExpect; // this runs third, returning a subscription object
callApi(apiObject) {
// this runs second and returns IMMEDIATELY a subscription object, NOT a result
return this.myApiService.someRandomPostApiCall( apiObject ).subscribe(res => {
console.log(res); // this runs fourth, and the return is completely useless: subscribe is not expected to return anything.
res.valid ? { return true } : { return false };
});
}
Upvotes: 1
Reputation: 60626
If you want to react when an item is emitted (basically waiting until the data is retrieved), your best bet is to subscribe in the component where you need it, not in the function itself.
return this.callApi( apiObject ).subscribe(//do stuff here);
callApi( apiObject ) {
return this.myApiService.someRandomPostApiCall( apiObject )
}
Here is an example from one of my applications:
In a service
getProducts(): Observable<IProduct[]> {
return this.http.get<IProduct[]>(this.productUrl)
.pipe(
tap(data => console.log('All: ' + JSON.stringify(data))),
catchError(this.handleError)
);
}
In a component
ngOnInit(): void {
this.productService.getProducts().subscribe({
next: products => this.products = products,
error: err => this.errorMessage = err
});
}
You can see the entire source here:
https://stackblitz.com/edit/github-angular-getting-started-final
Upvotes: 2
Reputation: 1368
You could call the function asynchronously like this. Which will return you a promise and need to be resolved. It will wait until get a response.
callApi(apiObject) {
return new Promise((resolve, reject) => {
this.api.someRandomPostApiCall(apiObject).subscribe(res => {
if (res.valid) {
resolve({status: true});
}
}, (error) => {
resolve({status: false});
},
() => {
});
});
}
Now you call it like this
// starts here
this.callApi(apiObject).then((res) => {
// ends here
console.log(res);
return res;
});
Or like this
async getData() {
return await this.callApi(apiObject);
}
You could also do the same in one function like this
callApi(apiObject) {
let promise = new Promise((resolve, reject) => {
let apiURL = `media=music&limit=20`;
this.http.get(apiURL)
.toPromise()
.then(
res => {
// Success
console.log(res);
resolve();
}, (msg) => {
// Error
reject(msg);
}
);
});
return promise;
}
}
Upvotes: 1