通过可观察到可观察的学习
从那以后,我已经重写了这篇文章,并将其移至我的个人博客:
https://benles.com/posts/learning-observable-by-building-observable/
我经常通过社交媒体或在活动中亲自接受有关“热”与“冷”可观察物的问题,或者可观察到的是“多播”或“单媒体”。人们似乎真的被“ rx.observable”的黑暗内部工作感到迷惑。当被要求描述一个可观察的人时,人们说“他们是流”或“他们就像承诺”之类的话。实际上,我有这些事情在许多情况下甚至是在公开谈话中说的。
与承诺的比较是必要的,但不幸。鉴于诺言和观察力都是异步原语,并且承诺已经被JavaScript社区广泛使用和熟悉,因此通常是一个很好的起点。将Promise的“然后”与可观察的``订阅''进行比较,显示了急切的vs vs vs vs执行的差异,显示了可观察结果的取消和重复使用等。这是一种将初学者介绍给可观察的方式。
有一个问题:可观察到的诺言与他们相似的诺言更有不同。承诺总是多播。承诺解决和拒绝始终是异步的。当人们接近可观察到的东西,好像他们与承诺相似时,他们会期望这些事情,但并不总是正确的。可观察到有时多播。可观察到通常异步。我有点责怪自己,我帮助持续了这种误解。
可观察的只是一个函数,该函数占据观察者并返回函数
如果您真的想了解可观察的,那么您可以简单地写一个。老实说,这并不像听起来那样难。可观察到的最小零件可观察到的,不过是具有特定目的的特定功能。
形状:
- 功能
- 接受一个观察者:带有``next',rorry'和``完整''方法的对象。
- 并返回取消功能
目的:
将观察者连接到产生价值的东西(生产者),并返回一种将这种连接与生产者拆除的手段。观察者实际上是可以随时间推动值推动值的处理程序的注册表。
基本实施:
功能myObservable(观察者){
const dataSource = new DataSource();
datasource.ondata =(e)=> observer.next(e);
datasource.onerror =(err)=> observer.error(err);
datasource.oncomplete =()=> observer.complete();
return()=> {
datasource.destroy();
};
}
如您所见,它没有很多。这是一项相当简单的合同。
安全的观察者:使观察者再次出色
当我们谈论RXJ或反应性编程时,通常可观察到的是最高的计费。但是观察者实现实际上是这种反应性编程的主力。观察物是惰性的。它们只是功能。他们坐在那里,直到您向他们订阅,他们设置了您的观察者,他们已经完成了,回到无聊的旧功能等待被称为。另一方面,观察者保持活跃并聆听制作人的活动。
You can subscribe to the observable now with any Plain-Old JavaScript Object (POJO) that has `next`, `error` and `complete` methods on it, but the POJO observer that you’ve used to subscribe to the observable is really just the beginning. In RxJS 5, we need to provide some guarantees for you. Below are just a few of the more important guarantees:
观察者保证
- 如果您通过观察者没有全部在实施的方法中,没关系。
- 您不想在完成``完整''或`error'之后呼叫`next``
- 如果您取消订阅,您不需要任何电话。
- 调用“完整”和“错误”需要调用未取消标准逻辑。
- 如果您的``next'',“完整”或“错误”处理程序会引发异常,则要调用您的未取消标准逻辑,以免泄漏资源。
- ``next','rorral'和`complete'实际上都是可选的。您不必处理所有值,错误或完成。您可能只想处理其中一两个事情。
为了实现这一目标,我们需要将您提供的匿名观察者包裹在执行上述保证的“ SafeObserver”中。由于上面的保证#2,我们需要跟踪是否调用了``plote''或`错误。由于#3,我们需要让我们的SafeObserver知道消费者何时表示要退订。最后,由于#4,我们的SafeObserver实际上需要知道关于无订阅逻辑,因此可以在调用`complete'或`错误时都称其为调用。
因此,如果我们想通过上述可观察到的临时函数实施来执行此操作,这将变得粗暴……这只是一个片段您可以从JSBIN玩耍,向您展示多么严重。在本示例中,我不希望(非常原始的)SafeObserver实施(在JSBIN中),因为它会吃掉整篇文章,但这只是我们使用SafeObserver的可观察到的:
功能myObservable(观察者){
const safeObserver = new SafeObserver(观察者);
const dataSource = new DataSource();datasource.ondata =(e)=> safeObserver.next(e);
datasource.onerror =(err)=> safeobserver.error(err);
datasource.oncomplete =()=> safeobserver.complete();
safeobserver.unsub =()=> {
datasource.destroy();
};
return safeObserver.unsubscribe.bind(safeObserver);
}
设计可观察:人体工程学观察者安全
将观察值作为类/对象作为类别,使我们能够轻松地应用一个SafeObserver来传递匿名观察者(如果您喜欢RXJS中的“订阅(FN,FN,FN,FN)”,则处理程序功能,并为开发人员使用者提供更好的人体工程学。通过处理可观察到的“订阅”实现内部的SafeObserver的创建,可以再次以最简单的方式定义可观察到的东西:
const myobservable = new observable((观察者)=> {
const dataSource = new DataSource();
datasource.ondata =(e)=> observer.next(e);
datasource.onerror =(err)=> observer.error(err);
datasource.oncomplete =()=> observer.complete();
return()=> {
datasource.destroy();
};
});
您会注意到上面的摘要看起来与我们的第一个示例几乎相同。它更容易阅读,更容易理解。我已经增加了我们的JSBIN示例显示最小可观察的实现。
操作员:也只有功能
RXJS中的“运算符”仅仅是一个可观察到的源的函数,并返回一个新的可观察到的可观察到的函数,该函数将在您订阅时订阅该源可观察到的源。我们可以像这样实施基本的独立操作员(再次在Jsbin):
功能图(源,项目){
返回新的观察值((观察者)=> {
const mapobserver = {
下一步:(x)=> observer.next(project(x)),
错误:(err)=> observer.error(err),
完成:()=> observer.complete()
};
return source.subscribe(mapObserver);
});
}
关于该操作员正在做什么的最重要的事情是:当您订阅其返回的可观察情况时,它会创建一个“ mapobserver”来完成工作并链接``observer''和``observer''和```observer''''和```observer''和```observer''''and``MapObserver''。构建操作员连锁店实际上只是创建一个模板,用于在订阅上一起接线观察者。
设计可观察:使操作员连锁店变得漂亮
如果我们要像上面的示例一样将所有运营商实施为独立函数,那么我们的操作员会变得有些丑陋:
地图(MAP(MyObservable,(x)=> x + 1),(x)=> x + 2);
现在想象以上,嵌套了五到六个操作员,深深地具有更复杂的操作员,这些操作员有更多的论点。基本上不可读。
您可以使用简单的“管道”实现(如德克萨斯州托兰德的建议)减少了这些操作员的数组来生成您的最终观察员,但这将意味着编写更复杂的操作员返回功能(JSBIN在这里的示例)。也不会看完美的任何一个:
管道(MyObservable,Map(X => X + 1),MAP(X => X + 2));
理想情况下,我们能够以更自然的方式链接事物:
myObservable.map(x => x + 1).map(x => x + 2);
幸运的是,我们已经有了一个可观察的课程,我们可以将操作员放在上面,以支持这种链接行为。它不会为操作员的实现增加任何复杂性,但是它确实是以“垃圾填补原型”为代价的,我想一旦您添加了足够的操作员,其中有很多 - 也许太多了。这是我们的地图操作员在可观察到的实现的原型中添加时的样子(与Jsbin):
observable.prototype.map = function(project){
返回新的观察值((观察者)=> {
const mapobserver = {
下一步:(x)=> observer.next(project(x)),
错误:(err)=> observer.error(err),
完成:()=> observer.complete()
};
返回this.subscribe(mapObserver);
});
};
现在我们有了我们要追求的更好的语法。这种方法还有其他好处,这些好处更为先进。例如,我们可以对特定类型的观察类型(可观察到的承诺或一组静态值)观察到亚类,并通过为操作员覆盖该子类对操作员进行优化。
TLDR:可观察物是一个函数,可以使用观察者并返回函数
请记住,在阅读上面的所有内容之后,所有这些都是围绕简单功能设计的。可观察物是一个函数,可以掌握观察者并返回函数。仅此而已。如果您编写一个符合观察者并返回函数的函数,那是异步还是同步?两者都不。这是一个功能。任何功能的行为都取决于其实施方式。因此,在处理可观察的时,将其视为您传递的功能参考,而不是某种魔术,状态的外星人类型。当您构建操作员连锁店时,您真正要做的就是组成一个函数,该功能设置了一系列观察者,这些函数与观察者链接在一起并传递给观察者。
注意:可观察到的实现示例仍然返回上面的功能,其中RXJ和ES-OBSERVABLE SPEC返回订阅对象。订阅对象是一个更好的设计,但是我可以写一整篇文章。我将其保留为取消订阅功能,只是为了使示例保持简单。