我如何在不订阅Observable的情况下获取当前值? 我只想获取当前值一次,而不是当新值出现时。
我如何在不订阅Observable的情况下获取当前值? 我只想获取当前值一次,而不是当新值出现时。
快速回答:
...我只想获取当前值一次,而不是随着新值的到来而获取...
您仍将使用subscribe
,但使用pipe(take(1))
,这样它就会给您一个单一的值。
例如:myObs$.pipe(take(1)).subscribe(value => alert(value));
另请参见:first()
、take(1)
或single()
之间的比较。
更长的回答:
subscribe()
从可观察对象中获取一个值(或者如果使用 Angular,可以使用 async 管道)
BehaviorSubject
绝对有它的用处。当我开始使用 RxJS 时,我经常使用 bs.value()
来获取值。随着你的 RxJS 流在整个应用程序中传播(这就是你想要的!),这将变得越来越困难。通常你会看到 .asObservable()
被用来“隐藏”底层类型,以防止某人使用 .value()
- 起初这似乎很严厉,但随着时间的推移,你会开始欣赏为什么要这样做。此外,你迟早需要某些不是 BehaviorSubject
的值,而没有办法让它成为 BehaviorSubject
。
但是回到最初的问题。尤其是如果您不想通过使用 BehaviorSubject
来“作弊”。
更好的方法总是使用 subscribe
来获取值。
obs$.pipe(take(1)).subscribe(value => { ....... })
或者
obs$.pipe(first()).subscribe(value => { ....... })
这两者之间的区别在于,first()
会在流已经完成时出错,而take(1)
则不会在流已完成或没有立即可用的值时发出任何observable。
注意:即使您正在使用BehaviorSubject,这也被认为是更好的做法。
然而,如果您尝试上述代码,则observable的“value”将被“卡住”在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);
需要注意的是,上面的订阅调用不会等待值。如果立即没有可用的内容,则订阅函数将永远不会被调用,并且我故意在那里放置了取消订阅调用以防止它“稍后出现”。
因此,这不仅看起来非常笨拙 - 对于不立即可用的东西(例如来自http调用的结果值),它也无法工作,但实际上它可以使用行为主题(或更重要的是已知为BehaviorSubject的上游内容,或者取两个BehaviorSubject值的combineLatest)。绝对不要做(obs$ as BehaviorSubject)
- 呕!
通常情况下,前面的示例仍然被认为是一种不好的做法 - 它很混乱。只有当我想查看一个值是否立即可用并能够检测到它不存在时,我才会使用前面的代码样式。
如果可能的话,您最好尽可能将所有内容保持为observable,并且仅在绝对需要该值时才进行订阅 - 不要尝试将值“提取”到包含范围中,这就是我上面所做的。
例如,假设我们想要制作一份关于我们动物的报告,如果你的动物园是开放的。你可能会认为你想要像这样提取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可观察对象,上面的代码将永远无法工作,无论您的服务器有多快 - 您都无法获得值!alert
锁定到此方法中。如果您必须在其他地方使用报告,则必须复制它!不要试图在函数中访问值,相反,考虑创建一个新的Observable函数,甚至不订阅它!
它返回一个可以在“外部”使用的新的可观察对象。
通过保持所有内容为可观察对象并使用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
内的操作。了解更多关于switchMap
的信息。switchMap
内部的代码必须返回一个新的可观察对象。您可以使用of('hello')
快速创建一个可观察对象,或者返回另一个可观察对象,例如combineLatest
。map
必须只返回常规字符串。当我开始心理记录直到我不得不这样做时,我突然开始编写更具生产力、灵活性、清洁度和可维护性的代码。
另一个最后的提示:如果您在Angular中使用此方法,可以通过使用| async
管道来获取上述动物园报告而不需要任何subscribe
。这是“在必须订阅之前不要订阅”的实践的绝佳示例。// in your angular .ts file for a component
const zooReport$ = this.getZooReport();
在你的模板中:
<pre> {{ zooReport$ | async }} </pre>
在这里也可以看到我的答案:
https://dev59.com/6VoU5IYBdhLWcg3wzZE1#54209999
此外为避免混淆,还有一些内容没有提到:
tap()
有时可能很有用,可以“从可观察对象中获取值”。 如果您不熟悉该操作符,请阅读相关文档。 RxJS 使用“管道”和 tap()
操作符是一种“进入管道”的方式以查看其中的内容。请查看https://benlesh.medium.com/rxjs-observable-interop-with-promises-and-async-await-bebb05306875中的“使用toPromise()和async/await将最后一个Observable值作为Promise发出”。
Angular 16 引入了一个新的 Signals 功能的预览。虽然不是完整的功能,但您已经可以在组件中开始使用 signals。其中一个设计目标就是解决这个“痛点”。
通过 customer = toSignal(customer$, { requireSync: true })
,您可以创建一个信号,当可观察值更改时更新,并使用 customer()
访问“立即”值,而无需上述常规样板文件。Angular 自动处理取消订阅可观察对象以实现此目的。
对此的想法:
()
访问信号以获取其值始终是同步的,因此如果您的可观察对象没有值,则会遇到上述某些问题。这就是 { requireSync: true }
的用途(有关更多信息,请查看 API 文档 - 可能会更改)。bs$.value
的情况。最后我总是陷入混乱之中。在最简单的情况下,你的代码可能今天可以使用行为主题,但明天你需要将其与其他输入或数据结合起来,突然间就不能再使用.value
了。虽然它能工作,但感觉笨拙,我再也不用它了。 - Simon_Weaver它将为您提供 Observable 发布的最新值。
- BehaviorSubject 类似于 ReplaySubject,但它仅记住最后一次发布的值。
- BehaviorSubject 还要求您提供 T 的默认值。这意味着所有订阅者将立即收到一个值(除非它已经完成)。
//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;
}
}
myState:EventEmitter<string> = new EventEmitter();
,所以我必须使用BehaviorSubject而不是EventEmitter,对吗?类似于myState:BehaviorSubject<string> = new BehaviorSubject('default state');
。 - EricCBehaviorSubject<T>
转换为Observable<T>
,并仅向其他人公开该可观察对象。这对您是否可以? - user276648const value = await this.observableMethod().toPromise();
由于toPromise()方法已经过时,这里介绍一种新的rxjs方法来替代:
let value = await lastValueFrom(someObservable);
或者
let value = await firstValueFrom(someObservable);
let value = await lastValueFrom(someObservable, {defaultValue: "some value"})
https://indepth.dev/posts/1287/rxjs-heads-up-topromise-is-being-deprecated
不确定这是否是您要寻找的内容。可能需要在服务中编写BehaviorSubject。将其声明为私有,并仅公开您设置的值。类似于以下内容:
@Injectable({
providedIn: 'root'
})
export class ConfigService {
constructor(private bname:BehaviorSubject<String>){
this.bname = new BehaviorSubject<String>("currentvalue");
}
getAsObservable(){
this.bname.asObservable();
}
}
这样,外部用户只能订阅behaviorSubject并且您可以在服务中设置所需的值。
@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);
});
}
.map
获取数据吗? - Pardeep Jain