angular - RXJS - BehaviorSubject:正确使用 .value
问题描述
我想知道是否真的有适当的用途behaviorSubject.value
。根据这个答案,我们应该只通过订阅来获取值。
对我来说似乎没问题的情况是我使用.value
来确定通过流推送的下一个值,例如当我切换一个简单的布尔值时:
myBoolSubject = new BehaviorSubject(false);
toggle() {
this.myBoolSubject.next(!this.myBoolSubject.value);
}
替代使用subscribe()
看起来像:
toggle() {
this.myBoolSubject.pipe(take(1)).subscribe(
val => this.myBoolSubject.next(!val)
);
}
通过查看rxjs 源代码和上述答案,这两种方法之间的区别在于.value
:
- 题目已经完成
- 出现错误
在这个简单的例子中,我不会完成这个主题,我不认为错误是一个问题,因为我只是通过这个主题推动简单的布尔值。
这是一个有效的用例behaviorSubject.value
吗?还有其他人吗?
另一种似乎可以使用.value
的情况是从先前发出的值构造新对象时:
private state = new BehaviorSubject<State>(INITIAL_STATE);
public state$ = this.state.asObservable();
public updateState(changes: Partial<State>){
const newState = {...this.state.value, ...changes};
this.state.next(newState);
}
另一种方法是将最新的状态发射缓存在另一个变量中,如下所示:
private _state = INITIAL_STATE;
private state = new BehaviorSubject<State>(INITIAL_STATE);
public state$ = this.state.asObservable();
public updateState(changes: Partial<State>){
const newState = {...this._state, ...changes};
this.state.next(this._state = newState);
}
有什么我忽略的问题吗?
解决方案
subscribe
1. 没有和使用的布尔示例.value
const { Subject } = rxjs;
const { scan, startWith } = rxjs.operators;
myToggle$$ = new Subject();
myBool$ = myToggle$$.pipe(
scan((acc) => !acc, false),
startWith(false)
)
myBool$.subscribe(v => console.log('result: ', v));
myToggle$$.next();
myToggle$$.next();
myToggle$$.next();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
2.反应式编程
避免复杂的有状态程序,在可观察的流上使用干净的输入/输出函数。
您已经在使用 rxjs,您也可以说,您想使用直接的命令式代码:
myBool = false;
function myToggle() { myBool = !myBool }
console.log('result: ', myBool);
myToggle();
console.log('result: ', myBool);
myToggle();
console.log('result: ', myBool);
myToggle();
console.log('result: ', myBool);
对你的.value
代码做了一件重要的事情,它不同于一个干净的反应式解决方案。业务逻辑从流中移到切换函数中。正如官方文档提到的那样,rxjs 的目标是拥有干净的输入/输出。这意味着input
你给你的每一个流接收总是相同的output
。这意味着您的转换应该在流内部处理,没有任何副作用。
3.副作用的缺点
- 更难的测试(嘲笑)
- 难以理解的影响
- 难以扩展功能(代码建立在动作之上,而不是专注于效果)
4. 使用 rxjs 更容易解决的问题
- 随着时间的推移异步/线程数据(取消,暂停,..)
- 运营商
- 不同流的简单组合(帮助您专注于应该发生的事情,而不是什么时候发生)
- 并发(轻松拆分和合并流)
5.我自己的看法
反应式编程为您处理了几个问题,或者至少使它更容易。在您展示的示例中,我看不出您为什么需要使用反应式编程。如果您完全避免rxjs
和编程,那也toggle()
完全没问题imperative
。但是,当您决定使用 aBehaviorSubject
时,您决定使用响应式编程。
6.总结
您can
通过使用实现您的问题,.value
但是您should not
。
推荐阅读
- firebase - Vue Firebase:currentUser 为空
- html - 将对象从视图传递到控制器
- cumulocity - 如何在 cumulocity 中使用 API 将“c8y_Address”属性添加到设备?
- trading - 在 Amibroker 回测中扩大购买头寸
- regex - 定义正则表达式以匹配多个名称变体
- python - 在一行python中输入整数和字符串
- android - 在圆形中绘制位图并填充外部彩色画布不起作用
- powershell - 将多个服务器名称传递到脚本中以同时运行
- keras - 如何查看gym.make('env')内部发生了什么
- python-3.x - 将 ONNX 模型导入 tensorflow-ValidationError: BatchNormalization.scale 在初始化程序中但不在图形输入中