无需订阅即可从 Observable 获取当前值(只需要一次值)
Get current value from Observable without subscribing (just want value one time)
如何在不订阅 Observable 的情况下获取当前值?我只想要一次当前值,而不是新值。
您需要使用BehaviorSubject,
- BehaviorSubject is similar to ReplaySubject except it only remembers the last publication.
- BehaviorSubject also requires you to provide it a default value of T. This means that all subscribers will receive a value immediately
(unless it is already completed).
它将为您提供 Observable 发布的最新值。
BehaviorSubject 提供了一个名为 value
的 getter
属性 来获取通过它的最新值。
- 在此示例中,值 '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
隐藏主题,只公开它的值
如果你想隐藏你的 BehaviorSubject 并且只公开它的值,比方说从一个服务,你可以像这样使用 getter。
export class YourService {
private subject = new BehaviorSubject('random');
public get subjectValue() {
return this.subject.value;
}
}
不确定这是否是您要查找的内容。可能在服务中写那个行为主题。将其声明为私有并仅公开您设置的值。像这样
@Injectable({
providedIn: 'root'
})
export class ConfigService {
constructor(private bname:BehaviorSubject<String>){
this.bname = new BehaviorSubject<String>("currentvalue");
}
getAsObservable(){
this.bname.asObservable();
}
}
这样外部用户只能订阅 behaviorSubject 并且您可以在服务中设置所需的值。
快速回答:
...I just want the current value one time and not new values as they are coming in...
您仍将使用 subscribe
,但使用 pipe(take(1))
,因此它会为您提供一个值。
例如。 myObs$.pipe(take(1)).subscribe(value => alert(value));
另请参阅:
之间的比较
更长的答案:
一般规则是你应该只从具有 subscribe()
的可观察值中获取值
(或异步管道,如果使用 Angular)
BehaviorSubject
绝对有它的位置,当我开始使用 RxJS 时,我经常使用 bs.value()
来得到一个值。随着你的 RxJS 流在你的整个应用程序中传播(这就是你想要的!),那么这样做会变得越来越难。通常你会看到 .asObservable()
习惯于 'hide' 底层类型 防止 某人使用 .value()
- 起初这看起来很刻薄,但你会开始明白为什么它会随着时间的推移而完成。此外,您迟早会需要 不是 和 BehaviorSubject
的值,而且没有办法做到这一点。
回到最初的问题。特别是如果你不想 'cheat' 使用 BehaviorSubject
.
更好的方法是始终使用 subscribe
来获取值。
obs$.pipe(take(1)).subscribe(value => { ....... })
或
obs$.pipe(first()).subscribe(value => { ....... })
这两者之间的区别是 first()
如果流已经完成 将出错 并且如果流已经完成 take(1)
将不会发出任何可观察值或者没有立即可用的值。
注意:这被认为是更好的做法,即使您使用的是 BehaviorSubject。
但是,如果您尝试上面的代码,可观察对象的 'value' 将在订阅函数的闭包内 'stuck',您可能在当前范围内需要它。如果你真的需要解决这个问题,一种方法是:
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);
重要的是要注意上面的订阅调用不会等待一个值。如果没有立即可用的订阅函数将永远不会被调用,我故意将取消订阅调用放在那里以防止它 'appearing later'.
所以这不仅看起来非常笨拙 - 它不适用于不能立即获得的东西,比如来自 http 调用的结果值,但它实际上可以用于行为主题(或更多重要的是 上游并且已知为 BehaviorSubject* 或采用两个 BehaviorSubject
值的 combineLatest
的东西)。绝对不要去做 (obs$ as BehaviorSubject)
- 呃!
这个前面的例子总体上仍然被认为是一种不好的做法——一团糟。如果我想查看一个值是否立即可用并且能够检测它是否可用,我只会使用以前的代码样式。
最佳方法
如果你能尽可能长时间地保持所有东西都是可观察的 - 并且只在你绝对需要这个值时订阅 - 而不是试图 'extract' 一个值到包含范围这就是我在上面所做的。
例如。比方说,如果您的动物园开放,我们想报告我们的动物。您可能认为您想要 zooOpen$
的 'extracted' 值,如下所示:
方法不对
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$
来自网络服务怎么办?前面的例子将如何工作?实际上,你的服务器有多快并不重要——如果 zooOpen$
是一个 http observable! ,你永远不会用上面的代码得到一个值
- 如果你想使用这个报告怎么办'outside'这个功能。您现在已将
alert
锁定到此方法中。如果您必须在其他地方使用该报告,则必须复制它!
好办法
与其尝试访问函数中的值,不如考虑创建一个新的 Observable 并且甚至不订阅它的函数!
它 return 是一个可以被消耗的新的可观察对象 'outside'。
通过将所有内容保持为可观察值并使用 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!');
}
});
}
上面的 创建了一个新的 observable 所以我们可以 运行 它在别处,如果我们愿意的话可以通过管道传输更多。
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()));
注意以下几点:
- 除非我绝对需要,否则我不会订阅 - 在这种情况下,这超出了功能
- 'driving' observable 是
zooOpen$
,它使用 switchMap
到 'switch' 到不同的 observable,最终是 return 从 getZooReport()
.
- 如果
zooOpen$
发生变化,它会取消所有内容并在第一个 switchMap
中重新开始。阅读有关 switchMap
的更多信息。
- 注意:
switchMap
中的代码必须 return 一个新的可观察对象。您可以使用 of('hello')
或 return 另一个可观察对象(例如 combineLatest
. 快速创建一个
- 同样:
map
必须只是 return 一个常规字符串。
当我开始记下在我 有 之前不要订阅时,我突然开始编写更高效、更灵活、更清洁和可维护的代码。
另一个最后的注意事项:如果您将此方法与 Angular 一起使用,则可以通过使用 | async
管道获得上述动物园报告而无需单个 subscribe
。这是实践中“在必须订阅之前不要订阅”原则的一个很好的例子。
// in your angular .ts file for a component
const zooReport$ = this.getZooReport();
并在您的模板中:
<pre> {{ zooReport$ | async }} </pre>
另请参阅我的回答:
为了避免混淆,上面也没有提到:
tap()
有时可能对 'get the value out of an observable' 有用。如果您不熟悉该运算符,请阅读它。 RxJS 使用 'pipes' 和 tap()
运算符是一种 'tap into the pipe' 查看那里有什么的方法。
使用 .toPromise() / async
参见 https://benlesh.medium.com/rxjs-observable-interop-with-promises-and-async-await-bebb05306875
中的 'Use toPromise() with async/await to emit the last Observable value as a Promise'
使用 Observable 构造函数创建任何类型的可观察流。当可观察对象的 subscribe() 方法执行时,构造函数将 运行 的订阅函数作为其参数。订阅者函数接收一个观察者对象,并可以将值发布到观察者的 next() method.Try this
@Component({
selector: 'async-observable-pipe',
template: '<div><code>observable|async</code>: Time: {{ time | async }} .
</div>'
})
export class AsyncObservablePipeComponent {
time = new Observable<string>((observer: Observer<string>) => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
const value = await this.observableMethod().toPromise();
如何在不订阅 Observable 的情况下获取当前值?我只想要一次当前值,而不是新值。
您需要使用BehaviorSubject,
- BehaviorSubject is similar to ReplaySubject except it only remembers the last publication.
- BehaviorSubject also requires you to provide it a default value of T. This means that all subscribers will receive a value immediately (unless it is already completed).
它将为您提供 Observable 发布的最新值。
BehaviorSubject 提供了一个名为 value
的 getter
属性 来获取通过它的最新值。
- 在此示例中,值 '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
隐藏主题,只公开它的值
如果你想隐藏你的 BehaviorSubject 并且只公开它的值,比方说从一个服务,你可以像这样使用 getter。
export class YourService {
private subject = new BehaviorSubject('random');
public get subjectValue() {
return this.subject.value;
}
}
不确定这是否是您要查找的内容。可能在服务中写那个行为主题。将其声明为私有并仅公开您设置的值。像这样
@Injectable({
providedIn: 'root'
})
export class ConfigService {
constructor(private bname:BehaviorSubject<String>){
this.bname = new BehaviorSubject<String>("currentvalue");
}
getAsObservable(){
this.bname.asObservable();
}
}
这样外部用户只能订阅 behaviorSubject 并且您可以在服务中设置所需的值。
快速回答:
...I just want the current value one time and not new values as they are coming in...
您仍将使用 subscribe
,但使用 pipe(take(1))
,因此它会为您提供一个值。
例如。 myObs$.pipe(take(1)).subscribe(value => alert(value));
另请参阅:
更长的答案:
一般规则是你应该只从具有 subscribe()
的可观察值中获取值
(或异步管道,如果使用 Angular)
BehaviorSubject
绝对有它的位置,当我开始使用 RxJS 时,我经常使用 bs.value()
来得到一个值。随着你的 RxJS 流在你的整个应用程序中传播(这就是你想要的!),那么这样做会变得越来越难。通常你会看到 .asObservable()
习惯于 'hide' 底层类型 防止 某人使用 .value()
- 起初这看起来很刻薄,但你会开始明白为什么它会随着时间的推移而完成。此外,您迟早会需要 不是 和 BehaviorSubject
的值,而且没有办法做到这一点。
回到最初的问题。特别是如果你不想 'cheat' 使用 BehaviorSubject
.
更好的方法是始终使用 subscribe
来获取值。
obs$.pipe(take(1)).subscribe(value => { ....... })
或
obs$.pipe(first()).subscribe(value => { ....... })
这两者之间的区别是 first()
如果流已经完成 将出错 并且如果流已经完成 take(1)
将不会发出任何可观察值或者没有立即可用的值。
注意:这被认为是更好的做法,即使您使用的是 BehaviorSubject。
但是,如果您尝试上面的代码,可观察对象的 'value' 将在订阅函数的闭包内 'stuck',您可能在当前范围内需要它。如果你真的需要解决这个问题,一种方法是:
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);
重要的是要注意上面的订阅调用不会等待一个值。如果没有立即可用的订阅函数将永远不会被调用,我故意将取消订阅调用放在那里以防止它 'appearing later'.
所以这不仅看起来非常笨拙 - 它不适用于不能立即获得的东西,比如来自 http 调用的结果值,但它实际上可以用于行为主题(或更多重要的是 上游并且已知为 BehaviorSubject* 或采用两个 BehaviorSubject
值的 combineLatest
的东西)。绝对不要去做 (obs$ as BehaviorSubject)
- 呃!
这个前面的例子总体上仍然被认为是一种不好的做法——一团糟。如果我想查看一个值是否立即可用并且能够检测它是否可用,我只会使用以前的代码样式。
最佳方法
如果你能尽可能长时间地保持所有东西都是可观察的 - 并且只在你绝对需要这个值时订阅 - 而不是试图 'extract' 一个值到包含范围这就是我在上面所做的。
例如。比方说,如果您的动物园开放,我们想报告我们的动物。您可能认为您想要 zooOpen$
的 'extracted' 值,如下所示:
方法不对
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$
来自网络服务怎么办?前面的例子将如何工作?实际上,你的服务器有多快并不重要——如果zooOpen$
是一个 http observable! ,你永远不会用上面的代码得到一个值
- 如果你想使用这个报告怎么办'outside'这个功能。您现在已将
alert
锁定到此方法中。如果您必须在其他地方使用该报告,则必须复制它!
好办法
与其尝试访问函数中的值,不如考虑创建一个新的 Observable 并且甚至不订阅它的函数!
它 return 是一个可以被消耗的新的可观察对象 'outside'。
通过将所有内容保持为可观察值并使用 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!');
}
});
}
上面的 创建了一个新的 observable 所以我们可以 运行 它在别处,如果我们愿意的话可以通过管道传输更多。
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()));
注意以下几点:
- 除非我绝对需要,否则我不会订阅 - 在这种情况下,这超出了功能
- 'driving' observable 是
zooOpen$
,它使用switchMap
到 'switch' 到不同的 observable,最终是 return 从getZooReport()
. - 如果
zooOpen$
发生变化,它会取消所有内容并在第一个switchMap
中重新开始。阅读有关switchMap
的更多信息。 - 注意:
switchMap
中的代码必须 return 一个新的可观察对象。您可以使用of('hello')
或 return 另一个可观察对象(例如combineLatest
. 快速创建一个
- 同样:
map
必须只是 return 一个常规字符串。
当我开始记下在我 有 之前不要订阅时,我突然开始编写更高效、更灵活、更清洁和可维护的代码。
另一个最后的注意事项:如果您将此方法与 Angular 一起使用,则可以通过使用 | async
管道获得上述动物园报告而无需单个 subscribe
。这是实践中“在必须订阅之前不要订阅”原则的一个很好的例子。
// in your angular .ts file for a component
const zooReport$ = this.getZooReport();
并在您的模板中:
<pre> {{ zooReport$ | async }} </pre>
另请参阅我的回答:
为了避免混淆,上面也没有提到:
tap()
有时可能对 'get the value out of an observable' 有用。如果您不熟悉该运算符,请阅读它。 RxJS 使用 'pipes' 和tap()
运算符是一种 'tap into the pipe' 查看那里有什么的方法。
使用 .toPromise() / async
参见 https://benlesh.medium.com/rxjs-observable-interop-with-promises-and-async-await-bebb05306875
中的 'Use toPromise() with async/await to emit the last Observable value as a Promise'使用 Observable 构造函数创建任何类型的可观察流。当可观察对象的 subscribe() 方法执行时,构造函数将 运行 的订阅函数作为其参数。订阅者函数接收一个观察者对象,并可以将值发布到观察者的 next() method.Try this
@Component({
selector: 'async-observable-pipe',
template: '<div><code>observable|async</code>: Time: {{ time | async }} .
</div>'
})
export class AsyncObservablePipeComponent {
time = new Observable<string>((observer: Observer<string>) => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
const value = await this.observableMethod().toPromise();