当前位置: 首页 > 知识库问答 >
问题:

无需订阅即可从Observable获取当前值(只需一次值)

南门峰
2023-03-14

我如何从一个可观察的对象中获取当前值而不订阅它?我只需要当前值一次,而不是新值。

共有3个答案

游安康
2023-03-14
const value = await this.observableMethod().toPromise();
郜谦
2023-03-14

你需要使用行为主体,

  • BehaviorSubject与ReplaySubject类似,只是它只记住最后一次发布

它将为您提供可观察对象发布的最新值。

BehaviorSubject提供一个名为valuegetter属性来获取通过它传递的最新值。

StackBlitz

  • 在本例中,值“a”被写入控制台:
//Declare a Subject, you'll need to provide a default value.
const subject: BehaviorSubject<string> = new BehaviorSubject("a");

用法:

console.log(subject.value); // will print the current value

如果你想隐藏你的行为主体,只暴露它的价值,比如说从服务中,你可以使用这样的getter。

export class YourService {
  private subject = new BehaviorSubject('random');

  public get subjectValue() {
    return this.subject.value;
  }
}
陶原
2023-03-14

快速回答:

…我只需要当前值一次,而不是新值,因为它们即将出现。。。

您仍将使用subscribe,但使用pipe(take(1))时,它会给您一个值。

例如,myObs$.pipe(take(1)).subscribe(value)=

另请参见:比较first()take(1)single()

详细回答:

(或异步管道(如果使用角度)

BehaviorSubject肯定有它的位置,当我开始使用RxJS时,我经常使用bs.value()来获取值。随着RxJS流在整个应用程序中传播(这正是您想要的!),这样做将变得越来越困难。通常,您会看到.asObservable()用于“隐藏”底层类型,以防止有人使用.value()——起初这似乎很重要,但您会开始理解为什么会随着时间的推移而这样做。此外,你迟早会需要一个不是行为主体的值,而且没有办法做到这一点。

不过,回到最初的问题。尤其是如果你不想通过使用行为主体来作弊。

更好的方法是始终使用subscribe获取值。

obs$.pipe(取(1)).subscribe(值)=

或者

obs$。管道(first())。订阅(value=

如果流已完成,则这两个值之间的差异为first()将出错;如果流已完成或没有立即可用的值,则take(1)将不会发出任何可见值。

注意:这被认为是更好的练习,即使你使用的是行为主体。

但是,如果您尝试上面的代码,observable的“值”将被“卡住”在subscribe函数的闭包中,并且您可能在当前范围内需要它。如果你真的必须这样做的话,解决这个问题的一个方法是:

const obsValue = undefined;
const sub = obs$.pipe(take(1)).subscribe(value => obsValue = value);
sub.unsubscribe();

// we will only have a value here if it was IMMEDIATELY available
alert(obsValue);

重要的是要注意,上面的subscribe调用并不等待值。如果现在没有可用的功能,那么subscribe函数将永远不会被调用,我特意将unsubscribe调用放在那里,以防止它“稍后出现”。

因此,这不仅看起来非常笨拙——它不适用于不立即可用的东西,比如http调用的结果值,而且它实际上适用于行为主题(或者更重要的是,它是一个上游的行为主体*,或者一个接受两个行为主体值的CombineRelateTest)并且绝对不要去做(obs$作为行为主体)-啊!

前面的例子总体上仍然被认为是一个糟糕的做法——一团糟。如果我想查看一个值是否立即可用,并且能够检测它是否可用,我只执行以前的代码样式。

如果您能够尽可能长时间地保持所有内容都是可观察的,并且只在您绝对需要值时订阅,而不是尝试将值“提取”到包含范围中,那么您的情况会好得多,这就是我在上面所做的。

eg.比方说,如果你的动物园开放,我们想报告我们的动物。你可能认为你想要的'提取'值的zooOpen$像这样:

zooOpen$: Observable<boolean> = of(true);    // is the zoo open today?
bear$: Observable<string> = of('Beary');
lion$: Observable<string> = of('Liony');

runZooReport() {

   // we want to know if zoo is open!
   // this uses the approach described above

   const zooOpen: boolean = undefined;
   const sub = this.zooOpen$.subscribe(open => zooOpen = open);
   sub.unsubscribe();

   // 'zooOpen' is just a regular boolean now
   if (zooOpen) 
   {
      // now take the animals, combine them and subscribe to it
      combineLatest(this.bear$, this.lion$).subscribe(([bear, lion]) => {

          alert('Welcome to the zoo! Today we have a bear called ' + bear + ' and a lion called ' + lion); 
      });
   }
   else 
   {
      alert('Sorry zoo is closed today!');
   }
}
  • 如果zooOpen$来自Web服务,该怎么办?前面的示例将如何工作?实际上,服务器的速度有多快并不重要——如果zooOpen$是一个可观察的http,那么上面的代码就永远不会有值
  • 如果您想在该函数的“外部”使用此报告,该怎么办。您现在已将警报锁定到此方法中。如果您必须在其他地方使用该报告,则必须复制该报告

与其试图访问函数中的值,不如考虑创建一个新的可观察的函数,甚至不订阅它。

取而代之的是,它返回一个可以在“外部”使用的新的可观察对象。

通过将一切都保持为可观察的,并使用switchMap做出决策,您可以创建新的可观察对象,这些可观察对象本身可以成为其他可观察对象的来源。

getZooReport() {

  return this.zooOpen$.pipe(switchMap(zooOpen => {

     if (zooOpen) {

         return combineLatest(this.bear$, this.lion$).pipe(map(([bear, lion] => {

                 // this is inside 'map' so return a regular string
                 return "Welcome to the zoo! Today we have a bear called ' + bear + ' and a lion called ' + lion;
              }
          );
      }
      else {

         // this is inside 'switchMap' so *must* return an observable
         return of('Sorry the zoo is closed today!');
      }

   });
 }

上面创建了一个新的可观察对象,因此我们可以在其他地方运行它,如果我们愿意的话,可以使用更多的管道。

 const zooReport$ = this.getZooReport();
 zooReport$.pipe(take(1)).subscribe(report => {
    alert('Todays report: ' + report);
 });

 // or take it and put it into a new pipe
 const zooReportUpperCase$ = zooReport$.pipe(map(report => report.toUpperCase()));

注意以下内容:

  • 在我绝对需要之前我不会订阅-在这种情况下,这超出了功能范围
  • “驱动”可观察对象是zooOpen$,它使用switchMap来“切换”到不同的可观察对象,该可观察对象最终是从getZooReport()返回的
  • 如果zooOpen$发生任何变化,则其工作方式将取消所有操作,并在第一个开关映射内重新启动。阅读有关开关地图的更多信息。
  • 注意:switchMap中的代码必须返回一个新的可观察值。您可以使用('hello')的快速创建一个对象,或者返回另一个可观察对象,例如combinelateest
  • 同样:map必须只返回一个常规字符串

当我开始在脑海中记下必须订阅才能订阅时,我突然开始编写更高效、更灵活、更干净和可维护的代码。

另一个最后注意事项:如果您使用Angular的这种方法,您可以通过使用|async管道获得上面的zoo报告,而无需单个订阅。这是一个很好的例子,说明了在实践中“必须先订阅才能订阅”的原则。

// in your angular .ts file for a component
const zooReport$ = this.getZooReport();

在模板中:

<pre> {{ zooReport$ | async }} </pre>

请参见我的回答:

https://stackoverflow.com/a/54209999/16940

为避免混淆,上文未提及:

  • tap()。如果您不熟悉该操作员,请仔细阅读。RxJS使用“管道”和tap()操作符是一种“轻敲管道”的方法,可以查看管道中有什么

请参阅中的“将toPromise()与async/await一起使用,以将最后一个可观察值作为promise发出”https://benlesh.medium.com/rxjs-observable-interop-with-promises-and-async-await-bebb05306875

 类似资料:
  • 问题内容: 我需要一个Java程序来获取 没有时间戳 的 当前日期 : 给我日期和时间戳。 但是我只需要日期,没有时间戳。我使用此日期与另一个没有时间戳的日期对象进行比较。 在印刷时 d应打印。 问题答案: 一个对象 是 一种时间戳的-它包含自00:00:00 1970年1月1日,UTC毫秒数。因此,您不能使用标准对象仅包含一天/一个月/一年而没有时间。 据我所知,在标准Java API中,没有真

  • 问题内容: 我想在应用程序中连接到wifi网络。 码: 但是问题是我不知道。如何获取WiFi网络的SSID ? 问题答案: 如果您想获得所有可用的wifi: 如果要连接wifi ssid: 如果您想添加新的wifi设置,我已在下面编写了演示应用程序:

  • 问题内容: 我想检查一个版本已更改/获取具有Dropbox上共享链接的文本文件的元数据。我不会使用dropbox api,因为它会使用户使用自己的帐户。我希望他们链接到我的帐户,但是我不能手动执行此操作,因为以后可能会更改密码。 所以:没有身份验证令牌,只需从Dropbox的共享链接获取元数据,以便我可以检查版本更改以及版本是否已更改,请下载新文件的内容。 也:我也乐意接受其他建议以使这项工作也可

  • 我需要找到一种方法,在不将图片保存到图库的情况下,将图片从相机捕获到位图中。我看到的所有使用“MediaStore.ACTION\u IMAGE\u CAPTURE”的方法都是在传递位图之前将捕获的图片保存到库中。

  • 问题内容: 我有一个Angular 2服务: 一切正常。但是我还有另一个不需要订阅的组件,它只需要在某个时间点获取isLoggedIn的当前值即可。我怎样才能做到这一点? 问题答案: 一个或没有的电流值。发出值时,将其传递给订户并使用它完成。 如果要使用当前值,请使用专门用于该目的的值。保留最后发出的值,并立即将其发送给新订户。 它还具有一种获取当前值的方法。

  • 问题内容: 例如,使用winamp(至少在Windows上),您可以在后台以winamp播放全屏游戏,并使用媒体按钮*控制声音。Winamp不需要聚焦,可以使游戏继续全屏显示。 我更喜欢用Java编写此代码,但这可能行不通(在Java afaik中很难捕获没有重点的击键),因此任何C#解决方案也都可以。 因此,基本问题是:如何在没有重点的情况下捕获击键? *)我相信“后退/前进/停止/邮件/搜索/