Jameson
Jameson

Reputation: 31

Calling a method which has subscribe how to wait for subscribe return type?

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

Answers (3)

Alberto Chiesa
Alberto Chiesa

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

DeborahK
DeborahK

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

sibabrat swain
sibabrat swain

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

Related Questions