什么时候在 rxjs 中使用 asObservable()?
When to use asObservable() in rxjs?
我想知道asObservable
有什么用:
根据文档:
An observable sequence that hides the identity of the
source sequence.
但是为什么需要隐藏序列?
何时使用 Subject.prototype.asObservable()
这样做的目的是防止从 API 中泄露主题的“观察者端”。基本上是为了防止在您不希望人们能够“下一步”进入结果可观察对象时出现泄漏抽象。
例子
(注意:这真的不是你应该如何将这样的数据源变成 Observable,而是你应该使用 new Observable
构造函数,见下文)。
const myAPI = {
getData: () => {
const subject = new Subject();
const source = new SomeWeirdDataSource();
source.onMessage = (data) => subject.next({ type: 'message', data });
source.onOtherMessage = (data) => subject.next({ type: 'othermessage', data });
return subject.asObservable();
}
};
现在,当有人从 myAPI.getData()
中获得可观察的结果时,他们无法 next
将值输入到结果中:
const result = myAPI.getData();
result.next('LOL hax!'); // throws an error because `next` doesn't exist
你通常应该使用 new Observable()
,不过
在上面的示例中,我们可能正在创建我们并非有意创建的内容。一方面,getData()
不像大多数可观察对象那样惰性,它会立即创建基础数据源 SomeWeirdDataSource
(可能还有一些副作用)。这也意味着,如果您 retry
或 repeat
结果可观察值,它不会像您认为的那样工作。
最好将数据源的创建封装在您的可观察对象中,如下所示:
const myAPI = {
getData: () => return new Observable(subscriber => {
const source = new SomeWeirdDataSource();
source.onMessage = (data) => subscriber.next({ type: 'message', data });
source.onOtherMessage = (data) => subscriber.next({ type: 'othermessage', data });
return () => {
// Even better, now we can tear down the data source for cancellation!
source.destroy();
};
});
}
使用上面的代码,可以使用 RxJS 的现有运算符在可观察对象之上组合任何行为,包括使其“不惰性”。
A Subject
可以同时充当 observer
和 observable
.
一个Obervable
有2个方法。
- 订阅
- 退订
只要您订阅 observable
,您就会收到 observer
上面有 next、error 和 complete 方法。
您需要隐藏序列,因为您不希望流源在每个组件中都公开可用。同样可以参考@BenLesh
的例子
P.S。 : 第一次来ReactiveJavascript的时候,看不懂asObservable
。因为我必须确保我清楚地了解基础知识,然后再去 asObservable
。 :)
除了 我还要提一下,我认为这取决于使用的语言。
对于像 JavaScript 这样的无类型(或弱类型)语言,通过创建像 [=11 这样的委托对象来向调用者隐藏源对象可能是有意义的=] 方法。尽管如果您考虑一下,它不会阻止调用者执行 observable.source.next(...)
。所以这种技术并不能防止 Subject API 泄漏,但它确实使它对调用者更加隐藏。
另一方面,对于像 TypeScript 这样的强类型语言,方法 asObservable()
似乎没有多大意义(如果有的话)。
静态类型语言通过简单地利用类型系统(例如接口)解决了 API 泄漏问题。例如,如果你的 getData()
方法被定义为 returning Observable<T>
那么你可以安全地 return 原来的 Subject
,调用者会得到一个编译错误如果尝试在其上调用 getData().next()
。
想想这个修改过的例子:
let myAPI: { getData: () => Observable<any> }
myAPI = {
getData: () => {
const subject = new Subject()
// ... stuff ...
return subject
}
}
myAPI.getData().next() // <--- error TS2339: Property 'next' does not exist on type 'Observable<any>'
当然,因为它在一天结束时全部编译为 JavaScript,所以在某些情况下您可能仍想创建委托。但我的观点是,这些情况的空间比使用 vanilla JavaScript 时要小得多,而且在大多数情况下你可能不需要这种方法。
(仅限 Typescript)使用类型而不是 asObservable()
我喜欢 关于使用类型的说法,所以我将添加一些额外的信息来澄清。
如果你使用asObservable(),那么你就是运行下面的代码
/**
* Creates a new Observable with this Subject as the source. You can do this
* to create customize Observer-side logic of the Subject and conceal it from
* code that uses the Observable.
* @return {Observable} Observable that the Subject casts to
*/
asObservable(): Observable<T> {
const observable = new Observable<T>();
(<any>observable).source = this;
return observable;
}
这对 Javascript 很有用,但 在 Typescript 中不需要。我将在下面解释原因。
示例
export class ExampleViewModel {
// I don't want the outside codeworld to be able to set this INPUT
// so I'm going to make it private. This means it's scoped to this class
// and only this class can set it.
private _exampleData = new BehaviorSubject<ExampleData>(null);
// I do however want the outside codeworld to be able to listen to
// this data source as an OUTPUT. Therefore, I make it public so that
// any code that has reference to this class can listen to this data
// source, but can't write to it because of a type cast.
// So I write this
public exampleData$ = this._exampleData as Observable<ExampleData>;
// and not this
// public exampleData$ = this._exampleData.asObservable();
}
两者做同样的事情,但不会向您的程序添加额外的代码调用或内存分配。
❌this._exampleData.asObservable();
❌
需要在运行时进行额外的内存分配和计算。
✅this._exampleData as Observable<ExampleData>;
✅
由类型系统处理,不会在运行时添加额外的代码或内存分配。
结论
如果您的同事尝试这样做,referenceToExampleViewModel.exampleData$.next(new ExampleData());
,那么它将在编译时被捕获并且类型系统不会让他们这样做,因为 exampleData$
已被强制转换为 Observable<ExampleData>
并且不再属于 BehaviorSubject<ExampleData>
类型,但他们将能够收听 (.subscribe()
) 该数据源或扩展它 (.pipe()
)。
当您只希望特定服务或 class 设置信息源,而不希望人们随机设置该信息源时,这很有用 willy-nilly代码库中的点。
我想知道asObservable
有什么用:
根据文档:
An observable sequence that hides the identity of the source sequence.
但是为什么需要隐藏序列?
何时使用 Subject.prototype.asObservable()
这样做的目的是防止从 API 中泄露主题的“观察者端”。基本上是为了防止在您不希望人们能够“下一步”进入结果可观察对象时出现泄漏抽象。
例子
(注意:这真的不是你应该如何将这样的数据源变成 Observable,而是你应该使用 new Observable
构造函数,见下文)。
const myAPI = {
getData: () => {
const subject = new Subject();
const source = new SomeWeirdDataSource();
source.onMessage = (data) => subject.next({ type: 'message', data });
source.onOtherMessage = (data) => subject.next({ type: 'othermessage', data });
return subject.asObservable();
}
};
现在,当有人从 myAPI.getData()
中获得可观察的结果时,他们无法 next
将值输入到结果中:
const result = myAPI.getData();
result.next('LOL hax!'); // throws an error because `next` doesn't exist
你通常应该使用 new Observable()
,不过
在上面的示例中,我们可能正在创建我们并非有意创建的内容。一方面,getData()
不像大多数可观察对象那样惰性,它会立即创建基础数据源 SomeWeirdDataSource
(可能还有一些副作用)。这也意味着,如果您 retry
或 repeat
结果可观察值,它不会像您认为的那样工作。
最好将数据源的创建封装在您的可观察对象中,如下所示:
const myAPI = {
getData: () => return new Observable(subscriber => {
const source = new SomeWeirdDataSource();
source.onMessage = (data) => subscriber.next({ type: 'message', data });
source.onOtherMessage = (data) => subscriber.next({ type: 'othermessage', data });
return () => {
// Even better, now we can tear down the data source for cancellation!
source.destroy();
};
});
}
使用上面的代码,可以使用 RxJS 的现有运算符在可观察对象之上组合任何行为,包括使其“不惰性”。
A Subject
可以同时充当 observer
和 observable
.
一个Obervable
有2个方法。
- 订阅
- 退订
只要您订阅 observable
,您就会收到 observer
上面有 next、error 和 complete 方法。
您需要隐藏序列,因为您不希望流源在每个组件中都公开可用。同样可以参考@BenLesh
的例子
P.S。 : 第一次来ReactiveJavascript的时候,看不懂asObservable
。因为我必须确保我清楚地了解基础知识,然后再去 asObservable
。 :)
除了
对于像 JavaScript 这样的无类型(或弱类型)语言,通过创建像 [=11 这样的委托对象来向调用者隐藏源对象可能是有意义的=] 方法。尽管如果您考虑一下,它不会阻止调用者执行 observable.source.next(...)
。所以这种技术并不能防止 Subject API 泄漏,但它确实使它对调用者更加隐藏。
另一方面,对于像 TypeScript 这样的强类型语言,方法 asObservable()
似乎没有多大意义(如果有的话)。
静态类型语言通过简单地利用类型系统(例如接口)解决了 API 泄漏问题。例如,如果你的 getData()
方法被定义为 returning Observable<T>
那么你可以安全地 return 原来的 Subject
,调用者会得到一个编译错误如果尝试在其上调用 getData().next()
。
想想这个修改过的例子:
let myAPI: { getData: () => Observable<any> }
myAPI = {
getData: () => {
const subject = new Subject()
// ... stuff ...
return subject
}
}
myAPI.getData().next() // <--- error TS2339: Property 'next' does not exist on type 'Observable<any>'
当然,因为它在一天结束时全部编译为 JavaScript,所以在某些情况下您可能仍想创建委托。但我的观点是,这些情况的空间比使用 vanilla JavaScript 时要小得多,而且在大多数情况下你可能不需要这种方法。
(仅限 Typescript)使用类型而不是 asObservable()
我喜欢
如果你使用asObservable(),那么你就是运行下面的代码
/**
* Creates a new Observable with this Subject as the source. You can do this
* to create customize Observer-side logic of the Subject and conceal it from
* code that uses the Observable.
* @return {Observable} Observable that the Subject casts to
*/
asObservable(): Observable<T> {
const observable = new Observable<T>();
(<any>observable).source = this;
return observable;
}
这对 Javascript 很有用,但 在 Typescript 中不需要。我将在下面解释原因。
示例
export class ExampleViewModel {
// I don't want the outside codeworld to be able to set this INPUT
// so I'm going to make it private. This means it's scoped to this class
// and only this class can set it.
private _exampleData = new BehaviorSubject<ExampleData>(null);
// I do however want the outside codeworld to be able to listen to
// this data source as an OUTPUT. Therefore, I make it public so that
// any code that has reference to this class can listen to this data
// source, but can't write to it because of a type cast.
// So I write this
public exampleData$ = this._exampleData as Observable<ExampleData>;
// and not this
// public exampleData$ = this._exampleData.asObservable();
}
两者做同样的事情,但不会向您的程序添加额外的代码调用或内存分配。
❌this._exampleData.asObservable();
❌
需要在运行时进行额外的内存分配和计算。
✅this._exampleData as Observable<ExampleData>;
✅
由类型系统处理,不会在运行时添加额外的代码或内存分配。
结论
如果您的同事尝试这样做,referenceToExampleViewModel.exampleData$.next(new ExampleData());
,那么它将在编译时被捕获并且类型系统不会让他们这样做,因为 exampleData$
已被强制转换为 Observable<ExampleData>
并且不再属于 BehaviorSubject<ExampleData>
类型,但他们将能够收听 (.subscribe()
) 该数据源或扩展它 (.pipe()
)。
当您只希望特定服务或 class 设置信息源,而不希望人们随机设置该信息源时,这很有用 willy-nilly代码库中的点。