事件,流,可观察对象,异步迭代器

当前,在JavaScript中处理一系列异步结果的唯一稳定方法是使用事件系统。 但是,正在开发三种替代方法:

信息流:[https://streams.spec.whatwg.org]
可观察到的:[https://tc39.github.io/proposal-observable]
异步迭代器:[https://tc39.github.io/proposal-async-iteration]

每个事件与其他事件相比有什么区别和好处?

这些中的任何一个是否打算替换事件?

2个解决方案
116 votes

这里大概有两类API:pull和push。

异步提取API非常适合从源中提取数据的情况。 该来源可能是文件,网络套接字,目录列表或其他任何内容。 关键是在询问时已完成从源中提取或生成数据的工作。

异步迭代器是此处的基本原语,它是基于拉取的异步源概念的一般表示。 在这样的来源中,您:

  • 通过执行{ value, done: false })从异步迭代器中提取
  • 使用{ value, done: false })(或使用{ value: undefined, done: true })等待结果
  • 检查结果以发现是异常(抛出),中间值({ value, done: false }))还是完成信号({ value: undefined, done: true })。

这类似于同步迭代器是基于拉式同步值源的概念的一般体现。 同步迭代器的步骤与上述步骤完全相同,省略了“等待结果”步骤。

可读流是异步迭代器的一种特殊情况,旨在专门封装I / O源,例如套接字/文件/等。 它们具有专用的API,用于将它们通过管道传递到可写流(代表I / O生态系统的另一半,接收器)并处理由此产生的背压。 它们也可以专门用于以有效的“自带缓冲区”方式处理字节。 这在某种程度上让人想起数组是同步迭代器的一种特例,它针对O(1)索引访问进行了优化。

Pull API的另一个功能是它们通常是单一消费者。 现在,无论谁拉取该值,它都将在源异步迭代器/流/等中不存在。 不再。 它已被消费者拉走。

通常,pull API提供了用于与某些基础数据源进行通信的接口,从而使消费者可以表达对它的兴趣。 这与...相反

当某些东西正在生成数据,并且生成的数据并不关心是否有人想要它时,推式API非常适合。 例如,无论是否有人感兴趣,鼠标移动然后单击某处仍然是事实。 您想通过推送API来体现这些事实。 然后,消费者(可能是他们中的多个)可能会订阅,以通知有关此类情况的通知。

API本身并不关心订阅者是零,一还是多个。 这只是表明有关宇宙中发生的事情的事实。

事件是对此的简单体现。 您可以在浏览器中订阅EventTarget,或在Node.js中订阅EventEmitter,并收到有关已调度事件的通知。 (通常但并非总是由EventTarget的创建者执行。)

Observable是EventTarget的更完善的版本。 他们的主要创新之处在于订阅本身由一流的对象Observable表示,然后您可以在其上应用组合器(例如过滤器,地图等)。 他们还选择将三个信号(通常称为下一个,完成和错误)捆绑在一起,并赋予这些信号特殊的语义,以便组合器尊重它们。 这与EventTarget相反,后者的事件名称没有特殊的语义(EventTarget的任何方法都不关心事件的名称是“ complete”还是“ asdf”)。 Node中的EventEmitter具有这种特殊语义方法的某些版本,其中“错误”事件会使进程崩溃,但这是很原始的。

可观察对象对事件的另一个不错的功能是,通常只有可观察对象的创建者才能使它生成下一个/错误/完成信号。 在EventTarget上,任何人都可以调用dispatchEvent()。 根据我的经验,责任的分离使代码更好。

但最后,事件和可观察的东西都是将事件推向世界的好API,这些API可以随时随地调出和调出。 我想说的是,可观察性是实现此目的的更现代的方式,并且在某些方面更胜一筹,但事件却更为普遍且易于理解。 因此,如果打算用任何东西代替事件,那将是可观察的。

推<->拉

值得注意的是,您可以在紧要关头构建任何一种方法:

  • 要在pull之上构建push,请不断地从pull API中进行pull,然后将这些块推送给任何使用者。
  • 要在push之上构建pull,请立即订阅push API,创建一个缓冲区来累积所有结果,当有人拉时,从该缓冲区中获取它。 (或者,如果您的使用者拉速比包装的推入API推入速度快,请等到缓冲区变为非空时。)

后者通常要比前者编写更多的代码。

试图在两者之间进行调整的另一方面是,只有pull API才能轻松传达背压。 您可以添加一个旁通道来推送API,以允许它们将背压传递回源。 我认为Dart可以做到这一点,有些人试图创建具有这种能力的可观察物的演化。 但是,与仅选择适当的pull API相比,IMO更加尴尬。 不利的一面是,如果您使用推API公开从根本上基于拉的源,则将无法传递背压。 顺便说一下,这是WebSocket和XMLHttpRequest API犯的错误。

总的来说,我发现尝试通过包装其他被误导的方法将所有内容统一到一个API中。 推和拉有各自不同的,并不十分重叠的区域,每个区域都可以正常工作,并且说我们应该选择您提到的四个API之一,并像某些人一样坚持使用,这是短视的,并且会导致代码笨拙。

Domenic answered 2020-07-29T06:13:47Z
5 votes

我对异步迭代器的理解有些局限,但是据我了解,WHATWG流是异步迭代器的特例。 有关此的更多信息,请参阅Streams API常见问题解答。 它简要介绍了与可观察对象的区别。

异步迭代器和可观察对象都是处理多个异步值的通用方法。 目前它们还没有互操作,但似乎正在考虑从异步迭代器创建Observable。基于可推的本质的可观察对象与当前事件系统更相似,AsyncIterables基于拉。 简化视图为:

-------------------------------------------------------------------------    
|                       | Singular         | Plural                     |
-------------------------------------------------------------------------    
| Spatial  (pull based) | Value            | Iterable<Value>            |    
-------------------------------------------------------------------------    
| Temporal (push based) | Promise<Value>   | Observable<Value>          |
-------------------------------------------------------------------------    
| Temporal (pull based) | await on Promise | await on Iterable<Promise> |
-------------------------------------------------------------------------    

我将AsyncIterables表示为Iterable<Promise>,以使类推更容易推理。 请注意await Iterable<Promise>没有意义,因为应该在for await...of AsyncIterator循环中使用它。

您可以在此处找到更完整的说明。

kirly answered 2020-07-29T06:14:22Z
translate from https://stackoverflow.com:/questions/39439653/events-vs-streams-vs-observables-vs-async-iterators