标题说明了一切。如何在不订阅的情况下从Observable获取当前值?我只想要当前值一次,而不是新值,因为它们正在进入......
答案 0 :(得分:25)
您需要使用BehaviorSubject,
- BehaviorSubject类似于ReplaySubject,只是它只记住上一个发布。
- BehaviorSubject还要求您为其提供默认值T.这意味着所有订阅者都将立即收到一个值 (除非它已经完成)。
它将为您提供Observable发布的最新值。
BehaviorSubject提供名为getter
的{{1}}属性,以获取通过它的最新值。
value
用法:
//Declare a Subject, you'll need to provide a default value.
var subject: BehaviorSubject<string> = new BehaviorSubject("a");
答案 1 :(得分:2)
快速解答:
...我只希望当前值一次而不是新值,因为它们要传入...
您仍将使用subscribe
,但使用pipe(take(1))
时,它将为您提供一个单一值。
例如myObs$.pipe(take(1)).subscribe(value => alert(value));
另请参阅:first()
, take(1)
or single()
更长的答案:
subscribe()
的可观察值中获取一个值(如果使用Angular,则为异步管道)
BehaviorSubject
肯定有它的位置,当我开始使用RxJS时,我经常会做bs.value()
来获取价值。随着您的RxJS流在整个应用程序中传播(这就是您想要的!),这样做将变得越来越困难。通常,您实际上经常会看到.asObservable()
用于“隐藏”基础类型以防止使用.value()
的人-起初这似乎很刻薄,但是您将开始欣赏为什么它随着时间的推移而完成。另外,您迟早会需要一个不是 BehaviorSubject
的值,并且没有办法实现这一点。
但是回到原始问题。尤其是如果您不想使用BehaviorSubject
来“作弊”。
更好的方法是始终使用subscribe
来获取价值。
obs$.pipe(take(1)).subscribe(value => { ....... })
OR
obs$.pipe(first()).subscribe(value => { ....... })
如果流已经完成,则这两个first()
之间的差将错误,并且如果流已经完成或没有流,take(1)
将不会发出任何可观察到的东西立即可用的值。
注意:即使您正在使用BehaviorSubject,这也被认为是更好的做法。
但是,如果您尝试上面的代码,则可观察对象的“值”将被“卡住”在订阅函数的闭包内,您很可能在当前作用域中需要它。解决这一问题的一种方法是:
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 **或采用两个combineLatest
值的BehaviorSubject
)。而且绝对不要去做(obs$ as BehaviorSubject)
-啊!
前面的示例通常仍然被认为是不好的做法-情况一团糟。仅当我想查看一个值是否立即可用并能够检测出它是否可用时,我才使用以前的代码样式。
如果您可以将所有内容保持在尽可能长的可观察范围内,那么您的境况要好得多;只有在您绝对需要该值时才订阅它,而不是尝试将一个值“提取”到一个包含作用域的范围内在上面。
例如假设您的动物园开放,我们想报告我们的动物。您可能会认为您想要这样的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$
来自网络服务怎么办?前面的示例将如何工作?实际上,您的服务器有多快并不重要-如果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
中的代码必须返回一个新的observable。您可以使用of('hello')
快速创建一个-或返回另一个可观察到的变量,例如combineLatest
。map
必须只返回常规字符串。我很快就开始刻意不要订阅,直到我拥有,我突然开始编写更具生产力,灵活性,简洁性和可维护性的代码。
另一个最后的注意事项:如果将这种方法与Angular结合使用,则可以使用subscribe
管道来获得上述Zoo报告而没有单个| async
。这是实践中“直到您必须先订阅”的一个很好的例子。
// 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()
运算符是一种“进入管道”以查看其中的内容的方式。答案 2 :(得分:1)
常量值=等待this.observableMethod()。toPromise();
答案 3 :(得分:0)
不确定这是否是您想要的。可能将该行为主题写入服务中。将其声明为私有并仅公开您设置的值。像这样
@Injectable({
providedIn: 'root'
})
export class ConfigService {
constructor(private bname:BehaviorSubject<String>){
this.bname = new BehaviorSubject<String>("currentvalue");
}
getAsObservable(){
this.bname.asObservable();
}
}
通过这种方式,外部用户仅具有订阅behaviorSubject的权限,您就可以在服务中设置所需的值。
答案 4 :(得分:0)
使用Observable构造函数创建任何类型的可观察流。构造函数将可观察者的subscription()方法执行时运行的订阅者函数作为参数。订阅者函数接收一个Observer对象,并且可以将值发布到观察者的next()方法。尝试此
@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);
});
}