【问题标题】:React Flux dispatcher vs Node.js EventEmitter - scalable?React Flux 调度程序与 Node.js EventEmitter - 可扩展?
【发布时间】:2015-09-06 11:15:41
【问题描述】:

当您使用 Node 的 EventEmitter 时,您订阅了一个事件。只有在触发特定事件时才会执行您的回调:

eventBus.on('some-event', function(data){
   // data is specific to 'some-event'
});

在 Flux 中,您向调度程序注册您的商店,然后在调度每个事件时调用您的商店。商店的工作是过滤它获得的每个事件,并确定该事件对商店是否重要:

eventBus.register(function(data){
   switch(data.type){
      case 'some-event':
            // now data is specific to 'some-event'
         break;
   }
});

In this video,主持人说:

“商店订阅操作。实际上,所有商店都接收所有操作,这就是它保持可扩展性的原因。”

问题

为什么以及如何将每个操作发送到每个商店 [大概] 比仅向特定商店发送操作更具可扩展性?

【问题讨论】:

    标签: node.js design-patterns reactjs flux eventemitter


    【解决方案1】:

    这里提到的可扩展性更多地是关于代码库的扩展,而不是软件速度方面的扩展。 Flux 系统中的数据很容易追踪,因为每个商店都注册到每个操作,并且这些操作定义了系统中可能发生的每个应用程序范围的事件。每个存储都可以确定它需要如何更新自己以响应每个操作,而程序员无需决定将哪些存储连接到哪些操作,并且在大多数情况下,您可以更改或读取存储的代码而无需担心关于它如何影响任何其他商店。

    在某些时候,程序员需要注册存储。存储非常特定于它将从事件中接收的数据。在商店内查找数据究竟比注册特定事件更好,让商店始终期待它需要/关心的数据?

    系统中的操作代表系统中可能发生的事情,以及该事件的相关数据。例如:

    • 用户登录;附带用户个人资料
    • 用户添加了评论;附带评论数据,它被添加到的项目 ID
    • 用户更新了帖子;自带post数据

    因此,您可以将操作视为商店可以了解的事物的数据库。任何时候调度一个动作,它都会被发送到每个商店。因此,在任何给定时间,您只需要一次考虑您的数据突变一个存储 + 操作。

    例如,当更新帖子时,您可能有一个 PostStore 来监视 POST_UPDATED 操作,当它看到它时,它会更新其内部状态以存储新帖子。这与任何其他可能也关心POST_UPDATED 事件的商店完全分开——任何其他开发该应用程序的团队的任何其他程序员都可以单独做出该决定,并且知道他们能够参与到可能发生的操作的数据库。

    这在代码库方面有用且可扩展的另一个原因是控制反转;每个商店决定它关心什么动作如何响应每个动作;所有数据逻辑都集中在该存储中。这与像 MVC 这样的模式形成对比,其中控制器被显式设置为调用模型上的变异方法,并且一个或多个 其他 控制器可能调用变异方法同时(或不同时间)在相同型号上;数据更新逻辑分布在整个系统中,理解数据流需要理解模型可能更新的每个地方。

    最后,要记住的另一件事是,注册与不注册是一种语义问题。将 store 接收所有操作的事实抽象出来是微不足道的。例如,在 Fluxxor 中,商店有一个名为 bindActions 的方法,它将特定操作绑定到特定回调:

    this.bindActions(
      "FIRST_ACTION_TYPE", this.handleFirstActionType,
      "OTHER_ACTION_TYPE", this.handleOtherActionType
    );
    

    即使商店接收到所有个动作,它还是会在内部映射中查找动作类型并在商店上调用适当的回调。

    【讨论】:

    • “每个商店都可以确定它需要如何更新自己以响应每个操作,而程序员无需决定将哪些商店连接到哪些操作。”这就是我不明白的。在某些时候,程序员需要注册存储。存储非常特定于它将从事件中接收的数据。在商店内查找数据究竟比注册特定事件更好,让商店始终期待它需要/关心的数据?
    • @rodrigo-silveira 我已经更新了更多信息;希望有帮助
    【解决方案2】:

    我一直在问自己同样的问题,但从技术上讲,除了简化之外,还看不出注册有什么好处。我将提出我对系统的理解,希望如果我错了,我可以得到纠正。

    TLDR; EventEmitter 和 Dispatcher 服务于类似的目的(发布/订阅),但他们将精力集中在不同的功能上。具体来说,Eve​​ntEmitter 不提供“waitFor”功能(它允许一个事件处理程序确保已经调用了另一个事件处理程序)。 Dispatcher 将精力集中在“waitFor”功能上。


    系统的最终结果是向商店传达已发生的操作。商店是“订阅所有事件,然后过滤”还是“订阅特定事件”(在调度程序处过滤)。应该不会影响最终结果。数据在您的应用程序中传输。 (处理程序总是只打开事件类型和进程,例如它不想对所有事件进行操作)

    正如你所说的“在某些时候,程序员将需要注册商店。”。这只是订阅保真度的问题。例如,我认为保真度的变化不会对“控制反转”产生任何影响。

    facebook 的 Dispatcher 中添加的(杀手)功能是它能够“等待”不同的商店,首先处理事件。问题是,这个功能是否要求每个商店只有一个事件处理程序?

    让我们看看这个过程。当你在 Dispatcher 上调度一个动作时,它(省略一些细节):

    • 迭代所有注册的订阅者(到调度员)
    • 调用注册的回调(每个商店一个)
    • 回调可以调用“waitfor()”,并传递一个“dispatchId”。这在内部引用了由不同商店注册的回调。这是同步执行的,导致另一个存储接收操作并首先更新。这要求在处理操作的代码之前调用“waitFor()”。
    • “waitFor”调用的回调会开启操作类型以执行正确的代码。
    • 回调现在可以运行它的代码,知道它的依赖项(其他商店)已经更新。
    • 回调会打开“类型”操作以执行正确的代码。

    这似乎是一种允许事件依赖的非常简单的方法。

    基本上所有的回调最终都会被调用,但是是以特定的顺序。然后切换到只执行特定代码。因此,就好像我们只以正确的顺序在每个商店上触发了“add-item”事件的处理程序。

    如果订阅处于回调级别(而不是“商店”级别),这仍然可能吗?这意味着:

    • 每个商店都会为特定事件注册多个回调,保持对它们的“dispatchTokens”的引用(与当前相同)
    • 每个回调都有自己的“dispatchToken”
    • 用户仍会“等待”特定回调,但会成为特定商店的特定处理程序
    • 然后,调度程序只需要以相同的顺序调度特定操作的回调

    Facebook 的聪明人可能已经发现,如果增加单个回调的复杂性,这实际上会降低性能,或者它可能不是优先事项。

    【讨论】:

      猜你喜欢
      • 2015-03-27
      • 2015-07-09
      • 1970-01-01
      • 2017-02-10
      • 2016-01-29
      • 2015-07-15
      • 2014-07-03
      • 2014-11-15
      • 2015-06-29
      相关资源
      最近更新 更多