How to return value from function which has Observable subscription inside?

227,539

Solution 1

EDIT: updated code in order to reflect changes made to the way pipes work in more recent versions of RXJS. All operators (take in my example) are now wrapped into the pipe() operator.

I realize that this Question was quite a while ago and you surely have a proper solution by now, but for anyone looking for this I would suggest solving it with a Promise to keep the async pattern.

A more verbose version would be creating a new Promise:

function getValueFromObservable() {
    return new Promise(resolve=>{
        this.store.pipe(
           take(1) //useful if you need the data once and don't want to manually cancel the subscription again
         )
         .subscribe(
            (data:any) => {
                console.log(data);
                resolve(data);
         })
    })
}

On the receiving end you will then have "wait" for the promise to resolve with something like this:

getValueFromObservable()
   .then((data:any)=>{
   //... continue with anything depending on "data" after the Promise has resolved
})

A slimmer solution would be using RxJS' .toPromise() instead:

function getValueFromObservable() {
    return this.store.pipe(take(1))
       .toPromise()   
}

The receiving side stays the same as above of course.

Solution 2

This is not exactly correct idea of using Observable

In the component you have to declare class member which will hold an object (something you are going to use in your component)

export class MyComponent {
  name: string = "";
}

Then a Service will be returning you an Observable:

getValueFromObservable():Observable<string> {
    return this.store.map(res => res.json());
}

Component should prepare itself to be able to retrieve a value from it:

OnInit(){
  this.yourServiceName.getValueFromObservable()
    .subscribe(res => this.name = res.name)
}

You have to assign a value from an Observable to a variable:

And your template will be consuming variable name:

<div> {{ name }} </div>

Another way of using Observable is through async pipe http://briantroncone.com/?p=623

Note: If it's not what you are asking, please update your question with more details

Solution 3

The problem is that data is captured inside the observable and I can just console log it. I want to return that value and console.log or whatever from different file by calling the function in which it resides.

Looks like you are looking for a "current value" getter inside an observable, when it emits and after an emission.

Subject and Observable doesn't have such a thing. When a value is emitted, it is passed to its subscribers and the Observable is done with it.

You may use BehaviorSubject which stores the last emitted value and emits it immediately to new subscribers.

It also has a getValue() method to get the current value;

Further Reading:

RxJS BehaviorSubject

How to get current value of RxJS Subject or Observable?

Solution 4

If you want to pre-subscribe to the same Observable which will be returned, just use

.do():

function getValueFromObservable() {
    return this.store.do(
        (data:any) => {
            console.log("Line 1: " +data);
        }
    );
}

getValueFromObservable().subscribe(
        (data:any) => {
            console.log("Line 2: " +data)
        }
    );

Solution 5

While the previous answers may work in a fashion, I think that using BehaviorSubject is the correct way if you want to continue using observables.

Example:

    this.store.subscribe(
        (data:any) => {
            myService.myBehaviorSubject.next(data)
        }
    )

In the Service:

let myBehaviorSubject = new BehaviorSubjet(value);

In component.ts:

this.myService.myBehaviorSubject.subscribe(data => this.myData = data)

I hope this helps!

Share:
227,539
Teddy
Author by

Teddy

Updated on August 04, 2022

Comments

  • Teddy
    Teddy almost 2 years

    I dont know how to extract value from Observable to be returned by function in which Observable is present. I need just a value from it to be returned, nothing else.

    Current version which works

    function getValueFromObservable() {
        this.store.subscribe(
            (data:any) => {
                console.log(data)
            }
        )
    }
    getValueFromObservable()
    

    I need this to work, function to return value, and then:

    function getValueFromObservable() {
        this.store.subscribe(
            (data:any) => {
                return data
            }
        )
    }
    console.log(getValueFromObservable())
    

    What am I doing wrong here?

  • Teddy
    Teddy almost 8 years
    Well not quite. The problem is that data is captured inside the observable and I can just console log it. I want to return that value and console.log or whatever from different file by calling the function in which it resides.
  • Jan B.
    Jan B. almost 8 years
    Andrei pointed out how to make the name available outside the callback by assigning it to the component's name variable. It is not possible to return name synchronously in your case.
  • ishandutta2007
    ishandutta2007 about 7 years
    @Matt: I can't use it in Oninit like that, What if I need to return explicitly, My calling code looks like this this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY‌​).map(toPayload).fnW‌​ithMultipleAsyncRetu‌​rns()
  • Jan B.
    Jan B. about 7 years
    @ishandutta2007 Hey there. You better create a new question on SO regarding your issue.
  • ishandutta2007
    ishandutta2007 about 7 years
    @Matt: created, in case you want to take a look (stackoverflow.com/questions/43381922/…)
  • ashok_khuman
    ashok_khuman over 6 years
    You can also use other operators like .map(data => data) which does the same thing and then subscribe it wherever you expect the result
  • Sohail
    Sohail about 6 years
    Is this.name can be accessible in any other function of this component, if I declared on top of this component as name : any ?
  • Andrei Zhytkevich
    Andrei Zhytkevich about 6 years
    @Sohail, yes, it will be accessible everywhere inside the component MyComponent, type doesn't matter.
  • Armando Perea
    Armando Perea about 6 years
    Here is a better link for using async Observable pipes than that blog: angular.io/guide/pipes
  • Armando Perea
    Armando Perea about 6 years
    I agree with ashok_khuman. Here is the guide angular.io/guide/pipes
  • Florian Leitgeb
    Florian Leitgeb over 5 years
    This could be a good answer, but in fact that you did not explain anything about it, makes it to a bad answer. What does "pre-subscribe" mean? And should it solve the question from thread opener?
  • hardywang
    hardywang over 5 years
    What is the return type of your getValueFromObservable function?
  • jparg
    jparg over 5 years
    Should be a promise with whatever type the store data is. e.g. Promise<StoreType>
  • Rick O'Shea
    Rick O'Shea over 5 years
    It's an interesting approach: use backward-compatibility feature to make the code compatible with your current knowledge, even for new code. I think he probably learned Observable usage since this post which would be preferable.
  • aruno
    aruno about 5 years
    note that in RxJS 6 do is now called tap and you must use it in a pipe. Also note that tap takes multiple parameters for different handlers such as next, complete and error.
  • Rogelio
    Rogelio about 5 years
    you're still returning a promise that needs to be resolved and not returning a value directly.
  • Memmo
    Memmo about 4 years
    Property 'take' does not exist on type 'Observable<>'
  • jparg
    jparg about 4 years
    @Roj I am sorry for the very late reply. Yes it is still returning a Promise. It simply is not possible to return a value directly (synchronously) if the value is retrieved asynchronously.
  • Thibault
    Thibault almost 4 years
    @Memmo try .pipe(take(1)) instead
  • Janos Vinceller
    Janos Vinceller over 3 years
    Why would I use asynchronousness in case of a data array?
  • Marinos An
    Marinos An over 3 years
    @JanosVinceller This is just an example, for focusing on other parts of the code. You can replace from(["someValue","someOtherValue"]) with an observable of your choice that emits more than one value. I guess, it might be more suitable to have used interval(1000).
  • djangofan
    djangofan over 3 years
    This answer is so confusing. The question was about Observable and the answer is using a Promise??