34

I've been banging my head against this one trying to figure it out, and no amount of documentation I've been able to read has given me an answer to my question.

I have a service which is speaking directly to an API and returning an observable event which under normal circumstances I would subscribe to and do what I want with the data, however in a secondary service which utilizes the requests from the restful service, I need to be able to return values from the request.

getSomething() {
    return this._restService.addRequest('object', 'method').run()
        .subscribe(
            res => {
                res;
            },
            err => {
                console.error(err);
            }
        );
}

returnSomething() {
    return this.getSomething();
}

In the quick example above, I want to know if there is any way I can return res from getSomething() within returnSomething(). If it's not achievable in this way, what is the alternative? I will add that the _restService is pretty heavily relied upon and I don't really want to start messing with that.

Sidriel
  • 1,160
  • 2
  • 9
  • 16
  • Where are you using `returnSomething()`? You know this is an asynchronous operation so you can't "get" the result immediately. You can maybe return the `(res)` inside subscribe in `returnSomething()` but i don't know if it'd be practical. – eko Jun 16 '16 at 18:55
  • The example above would be contained within a service, and the call to the method would be in multiple components, which is why I would like to just return the `res` value, as it would save quite a bit of code. I'd also need to be able to compare the results of two or more similar methods. – Sidriel Jun 16 '16 at 18:57

3 Answers3

49

Since http calls and the like are async, you get an Observable instead of a synchronous value returned. You have to subscribe to it, and in the callback in there you get the data. There is no way around that.

One option would be to place your logic in the subscribe call

getSomething() {
    return this._restService.addRequest('object', 'method').run()
        .subscribe(
            res => {
                // do something here
                res;
            },
            err => {
                console.error(err);
            }
        );
}

But the way I like doing it is to add a callback, to inject the logic from outside (maybe a component, maybe another service):

getSomething(callback: (data) => void) {
    return this._restService.addRequest('object', 'method').run()
        .subscribe(
            res => {
                callback(res);
            },
            err => {
                console.error(err);
            }
        );
}

And in your component or wherever:

this._yourService.getSomething((data) => {
    // do something here
    console.log(data);
});
Maximilian Riegler
  • 22,720
  • 4
  • 62
  • 71
4

I had a similar issue. My solution was to convert the observable into a promise with .toPromise() (and use async - await to return it and catch errors). You can convert your code into something like:

 async getSomething() {
     try{
         return await this._restService.addRequest('object', 'method').run().toPromise()
      } catch (err){
       console.error(err);
   }
  }
hjbello
  • 643
  • 4
  • 18
  • This is not a solution for the initial question. You do not return the data that way, but a `Promise` and you would still have to "subscribe" to it (with `then`) to get the final data. – Johannes Sep 15 '20 at 11:39
0

I didn't use this myself, but I believe it should work in theory (:

// service.ts
getSomething() {
  let subject: Subject = new Subject();
  this._restService.addRequest('object', 'method').run()
    .subscribe(subject);
  return subject;
}

// this can be removed (;
returnSomething() {
  return this.getSomething();
}

// component.ts
ngOnInit() {
  this.service.returnSomething()
    .subscribe(res => console.log(res), err => console.log(err));
}

Check subject docs for more info. You can use different types of subject, for example BehaviorSubject has value property you can access...

ngOnInit() {
  // if you use BehaviorSubject
  this.service.returnSomething().value
}

Here's the working plunker...

Sasxa
  • 40,334
  • 16
  • 88
  • 102
  • This won't work, since subject gets returned before you get the data from subscribing to it. – Maximilian Riegler Jun 16 '16 at 19:09
  • @rinukkusu True, for initial value, should work after.. You can set initial value for *BehaviourSubject* tho'... Or `.filter(Boolean)` before subscribing to the observable... – Sasxa Jun 16 '16 at 19:12