如何从内部具有Observable订阅的函数返回值?


96

我不知道如何从Observable中提取值以由存在Observable的函数返回。我只需要从中返回一个值,别无其他。

当前版本有效

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

我需要这个工作,函数返回值,然后:

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

我在这里做错了什么?


2
您应该返回一个Observable / Promise,并在解决Observable时通过它传递数据
galvan 2016年

2
您可以为此放置一些简单的代码吗?
泰迪

6
您想要实现的是一种反模式:您试图“同步”一个异步任务。那不是可观察物应该工作的方式。简而言之,在大多数情况下,具有可观察值作为输入的函数也应返回可观察值-或不返回任何内容。当您需要对输出进行某些操作时,请订阅它。在这种情况下,如果您要console.log数据,则只需在内部进行即可subscribe
Nguyen可以

1
我明白你说的一切。我只是使用控制台日志作为演示,我将进一步使用该数据,这就是为什么我需要它来在可观察范围之外控制台日志。关键是要有一个功能,当您可以在该功能中订阅可观察的内容,获取数据,取消订阅并返回数据时,我就可以进一步使用该数据。我知道它是反模式的,但是我需要它来工作。任何帮助表示赞赏。当前,我的解决方案有效,但是我对此不太有信心。
泰迪

4
请注意!“解决方案”部分中的代码绝对不正确。不要使用它!仅当this.store.subscribe((data:any)=> {output = data}).unsubscribe()部分完成直到返回为止,它才有效。否则它将返回未定义。
Rodion Golovushkin

Answers:


56

编辑:更新了代码,以反映对RXJS的最新版本中管道工作方式所做的更改。现在,所有运算符(以我的示例为例)都包装在pipe()运算符中。

我意识到这个问题已经有一段时间了,您现在肯定已经找到了适当的解决方案,但是对于任何寻求此问题的人,我建议使用Promise解决该问题,以保持异步模式。

更详细的版本将创建一个新的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);
         })
    })
}

然后,在接收端,您将“等待”承诺以类似以下方式解决:

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

一个更苗条的解决方案是使用RxJS的.toPromise()代替:

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

接收方当然与上述相同。


getValueFromObservable函数的返回类型是什么?
hardywang

无论存储数据是哪种类型,都应该是一个承诺。例如Promise <StoreType>
jparg

4
您仍在返回需要解决的承诺,而不是直接返回值。
罗伊

1
属性“ take”在类型“ Observable <>”上不存在
Memmo

1
@Memmo试试.pipe(take(1))代替
Thibault

20

这不是使用的完全正确的想法 Observable

在组件中,您必须声明将持有对象的类成员(将在组件中使用的东西)

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

然后a Service将为您返回Observable

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

Component 应该做好准备以便能够从中检索值:

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

您必须将的值分配给Observable变量:

并且您的模板将使用变量name

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

另一种使用方式Observable是通过async管道http://briantroncone.com/?p=623

注意:如果不是您要的内容,请用更多详细信息更新您的问题


好吧,不完全是。问题在于数据是在可观察的内部捕获的,我可以通过控制台对其进行记录。我想通过调用它所驻留的函数来返回该值和console.log或其他文件中的任何内容。
泰迪

Andrei指出了如何name通过将回调函数分配给组件的name变量来使其在回调函数外部可用。根据name您的情况,不可能同步返回。
马特

@Matt:我不能那样使用它Oninit,如果我需要显式返回,我的调用代码将如下所示 this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
ishandutta2007 '17

@ ishandutta2007嘿。您最好在SO上针对您的问题创建一个新问题。
马特

@马特:创建的,如果你想看看(stackoverflow.com/questions/43381922/...
ishandutta2007

7

如果您想预订将返回的相同Observable,只需使用

。做():

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

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

3
您还可以使用其他运算符,例如.map(data => data)执行相同操作的操作,然后在期望结果的任何地方订阅它
ashok_khuman,

我同意ashok_khuman。这里是指导angular.io/guide/pipes
阿曼多·佩雷亚

这可能是一个很好的答案,但是实际上您对此没有任何解释,因此答案很差。“预订阅”是什么意思?它是否应该解决线程打开器中的问题?
Florian Leitgeb

请注意,do现在已在RxJS 6 中调用tap它,并且必须在管道中使用它。另外请注意,tap需要针对不同的处理程序,如多个参数nextcompleteerror
Simon_Weaver

6

问题在于数据是在可观察的内部捕获的,我可以通过控制台对其进行记录。我想通过调用它所驻留的函数来返回该值和console.log或其他文件中的任何内容。

好像您在观察对象内(发射时和发射后)正在寻找“当前值”吸气剂一样。

Subject而且Observable没有这样的东西。发出值时,将其传递给其订户并Observable使用它来完成。

您可以使用BehaviorSubject哪个存储最后发出的值,并立即将其发出给新的订户。

它还有一种getValue()获取当前值的方法。

进一步阅读:

RxJS BehaviorSubject

如何获取RxJS Subject或Observable的当前值?


2

可以从任何位置检索可观察的值。首先源序列到能够在其他地方发射的特殊观察器上。这是通过Reactive Extensions(RxJS)中的Subject类实现的。

var subject = new Rx.AsyncSubject();  // store-last-value method

将价值储存在观察者身上

subject.next(value); // store value
subject.complete(); // publish only when sequence is completed

要从其他地方获取值,请像这样订阅观察者:

subject.subscribe({
  next: (response) => {
      //do stuff. The property name "response" references the value
  }
});

主题既是可观察者又是观察者。对于其他使用场景,还有其他主题类型,例如BehaviourSubject和ReplaySubject。

不要忘记导入RxJS。

var Rx = require('rxjs');

1

尽管前面的答案可能以某种方式起作用,但我认为,如果您想继续使用可观察对象,那么使用BehaviorSubject是正确的方法。

例:

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

在服务中:

let myBehaviorSubject = new BehaviorSubjet(value);

在component.ts中:

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

我希望这有帮助!


0

例如,这是我的html模板:

<select class="custom-select d-block w-100" id="genre" name="genre"
                  [(ngModel)]="film.genre"
                  #genreInput="ngModel"
                  required>
            <option value="">Choose...</option>
            <option *ngFor="let genre of genres;" [value]="genre.value">{{genre.name}}</option>
          </select>

这是与我的组件中的模板绑定的字段:

  // Genres of films like action or drama that will populate dropdown list.
  genres: Genre[];

我从服务器动态获取电影流派。为了与我创建的服务器进行通信FilmService

这是与服务器通信的方法:

 fetchGenres(): Observable<Genre[]> {
    return this.client.get(WebUtils.RESOURCE_HOST_API + 'film' + '/genre') as Observable<Genre[]>;
  }

为什么此方法Observable<Genre[]>不返回类似内容Genre[]

JavaScript是async,它不等待昂贵的过程后返回值的方法。昂贵的意思是一个需要花费时间才能返回价值的过程。就像从服务器获取数据一样。因此,您必须返回Observable的引用并进行订阅。

例如在我的组件中:

ngOnInit() {
    this.filmService.fetchGenres().subscribe(
      val => this.genres = val
    );
  }

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

在上述情况下,console.log在承诺未解决之前运行,因此不会显示任何值,请将其更改为以下内容

function getValueFromObservable() {
    return this.store
}

getValueFromObservable()
 .subscribe((data: any) => {
    // do something here with data
    console.log(data);
});

另一种解决方案是,当您需要在getValueFromObservable内的数据以返回操作符的可观察值并订阅该函数时。

 function getValueFromObservable() {
        return this.store.subscribe((data: any) => {
            // do something with data here
            console.log(data);
            //return again observable.
            return of(data);
       })
    }

    getValueFromObservable()
     .subscribe((data: any) => {
        // do something here with data
        console.log(data);
    });

0

在javascript的单线程,异步,面向承诺,反应趋势的世界中async/await,命令式程序员最好的朋友是:

(async()=>{

    const store = of("someValue");
    function getValueFromObservable () {
        return store.toPromise();
    }
    console.log(await getValueFromObservable())

})();

并且store是多个值的序列:

  const aiFrom = require('ix/asynciterable').from;
  (async function() {

     const store = from(["someValue","someOtherValue"]);
     function getValuesFromObservable () {
        return aiFrom(store);
     }
     for await (let num of getValuesFromObservable()) {
       console.log(num);
     }
  })();

0

体面的方法是从函数返回可观察值,并在需要时进行订阅,因为可观察值是惰性的,因此它们只有在被订阅时才会开始发出值。

在这里,我有一个更有趣的事件驱动解决方案,我最初使用它。下面的示例通过使用nodejs的“ events ”模块来做到这一点。您可以将其与存在类似模块的其他框架一起使用注意:语法和样式可能会根据所使用的模块而改变)。

var from =require("rxjs").from;
var map = require("rxjs/operators").map;
var EventEmitter = require("events");

function process(event) {
    from([1,2,3]).pipe(
        map(val => `The number is:: ${val}`)
    ).subscribe((data) => {
       event.emit("Event1", data); //emit value received in subscribe to the "Event1" listener
    });
}

function main() {
   class Emitter extends EventEmitter{};
    var event = new Emitter(); //creating an event
    event.on("Event1", (data)=>{ //listening to the event of name "Event1" and callback to log returned result
        console.log(data); //here log, print, play with the data you receive
    });
    process(event); //pass the event to the function which returns observable.
}

main(); //invoke main function

这只是展示一个想法的示例,其中我们可以通过发出和收听的方法从不同的地方传递数据。这也称为事件驱动代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.