【问题标题】:How to return value from function which has Observable subscription inside?如何从内部具有 Observable 订阅的函数返回值?
【发布时间】:2016-11-12 12:08:21
【问题描述】:

我不知道如何从 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())

我在这里做错了什么?

【问题讨论】:

  • 你应该返回一个 Observable/Promise 并在你的 observable 被解析时通过它传递数据
  • 你能写一些简单的代码吗?
  • 您想要实现的是反模式:您正在尝试“同步”异步任务。这不是可观察的工作方式。简而言之,在大多数情况下,一个以 observable 作为输入的函数也应该返回一个 observable——或者什么也不返回。当你需要对输出做一些事情时,订阅它。在这种情况下,如果你想 console.log 数据,只需在 subscribe
  • 我明白你所说的一切。我只是使用控制台日志作为演示,我将进一步使用该数据,这就是为什么我需要它在可观察对象之外进行控制台日志。关键是要有一个函数,当你可以订阅 observable、获取数据、取消订阅并在该函数中返回数据时,我可以进一步使用该数据。我知道这是反模式,但我需要它工作。任何帮助表示赞赏。目前我的解决方案有效,但我对此并不太自信。
  • 请注意! “解决方案”部分的代码绝对不正确。不要使用它!仅当 this.store.subscribe( (data:any) => { output = data } ).unsubscribe() 部分完成直到返回时,它才会起作用。否则它将返回 undefined。

标签: typescript angular rxjs rxjs5


【解决方案1】:

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

在组件中,您必须声明将包含一个对象的类成员(您将在组件中使用的东西)

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

然后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

注意:如果这不是您所要求的,请更新您的问题并提供更多详细信息

【讨论】:

  • 不完全是。问题是数据是在 observable 中捕获的,我可以通过控制台记录它。我想通过调用它所在的函数来返回该值和 console.log 或来自不同文件的任何内容。
  • Andrei 指出了如何通过将 name 分配给组件的 name 变量来使回调外部可用。在您的情况下,无法同步返回name
  • @Matt:我不能在Oninit这样使用它,如果我需要显式返回怎么办,我的调用代码看起来像这样this.actions$.ofType(SearchActions.SEARCH_MULTIPLE_NEW_QUERY).map(toPayload).fnWithMultipleAsyncReturns()
  • @ishandutta2007 你好。您最好针对您的问题创建一个关于 SO 的新问题。
  • @Matt: 已创建,以防你想看看 (stackoverflow.com/questions/43381922/…)
【解决方案2】:

编辑:更新代码以反映在最新版本的 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 函数的返回类型是什么?
  • 你仍然返回一个需要解决的承诺,而不是直接返回一个值。
  • 属性 'take' 不存在于类型 'Observable'
  • @Memmo 尝试 .pipe(take(1)) 代替
  • 这个答案太混乱了。问题是关于 Observable 的,答案是使用 Promise??
【解决方案3】:

可以从任何位置检索可观察值。源序列首先被 推送 到一个特殊的 observer 上,该观察者可以在其他地方发射。这是通过 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
  }
});

Subjects 既是 Observables 又是 Observers。还有其他的Subject types,比如 BehaviourSubject 和 ReplaySubject 用于其他使用场景。

别忘了导入 RxJS。

var Rx = require('rxjs');

【讨论】:

    【解决方案4】:

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

    .do():

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

    【讨论】:

    • 您还可以使用其他运算符,例如 .map(data =&gt; data),它执行相同的操作,然后在您期望结果的任何地方订阅它
    • 我同意ashok_khuman。这是指南angular.io/guide/pipes
    • 这可能是一个很好的答案,但事实上你没有解释任何关于它的东西,使它成为一个糟糕的答案。 “预订阅”是什么意思?它应该解决线程开启者的问题吗?
    • 请注意,在 RxJS 6 中,do 现在称为 tap,您必须在管道中使用它。另请注意,tap 为不同的处理程序采用多个参数,例如 nextcompleteerror
    【解决方案5】:

    例如这是我的 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&lt;Genre[]&gt; 而不是 Genre[]

    JavaScript 是async,它不会等待一个方法在一个昂贵的过程之后返回值。昂贵的意思是一个需要时间来返回价值的过程。就像从服务器获取数据一样。所以你必须返回 Observable 的引用并订阅它。

    例如在我的组件中:

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

    【讨论】:

      【解决方案6】:

      问题是数据是在 observable 中捕获的,我可以通过控制台记录它。我想通过调用它所在的函数来返回该值和 console.log 或来自不同文件的任何内容。

      看起来您正在可观察对象内寻找“当前值”getter,当它发射时和发射后。

      SubjectObservable 没有这样的东西。当一个值被发出时,它被传递给它的订阅者,Observable 就完成了。

      您可以使用 BehaviorSubject 存储最后发出的值并立即将其发送给新订阅者。

      它还有一个getValue()方法来获取当前值;

      进一步阅读:

      RxJS BehaviorSubject

      How to get current value of RxJS Subject or Observable?

      【讨论】:

        【解决方案7】:

        虽然前面的答案可能以某种方式起作用,但我认为如果您想继续使用 observables,使用 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)
        

        我希望这会有所帮助!

        【讨论】:

          【解决方案8】:

          在单线程、异步、面向承诺、反应式趋势的 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);
               }
            })();
          

          【讨论】:

          • 为什么我要在数据数组的情况下使用异步?
          • @JanosVinceller 这只是一个示例,用于关注代码的其他部分。您可以将from(["someValue","someOtherValue"]) 替换为您选择的可发出多个值的可观察对象。我想,使用interval(1000) 可能更合适。
          【解决方案9】:
          function getValueFromObservable() {
              this.store.subscribe(
                  (data:any) => {
                      return data
                  }
              )
          }
          console.log(getValueFromObservable())
          

          在上述情况下,console.log 在 promise 解决之前运行,因此不显示任何值,请将其更改为 follow

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

          另一种解决方案是当您需要 getValueFromObservable 中的数据以使用 of 运算符返回可观察对象并订阅该函数时。

           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);
              });
          

          【讨论】:

            【解决方案10】:

            体面的方法是从函数返回 observable 并在需要的地方订阅它,因为 observable 是惰性的,它们只有在订阅时才会开始发出值。

            这里我有一个更有趣的事件驱动解决方案,我最初用来玩弄它。以下示例通过使用 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
            

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

            【讨论】:

              猜你喜欢
              • 2017-02-17
              • 2019-03-09
              • 1970-01-01
              • 2017-03-26
              • 1970-01-01
              • 1970-01-01
              • 2017-11-24
              相关资源
              最近更新 更多