【问题标题】:Why use event listeners over function calls?为什么在函数调用上使用事件监听器?
【发布时间】:2010-12-21 20:43:36
【问题描述】:

我最近一直在研究事件监听器,我想我终于搞定了。基本上,它们是在另一个对象的方法上调用的函数。我的问题是,为什么在调用函数时创建一个事件监听器就可以了?

例如,我想调用 player.display_health(),当它被触发时,方法 player.get_health() 应该被触发并存储,以便 display_health() 可以访问它。为什么我应该使用事件侦听器而不是简单地调用函数?即使 display_health() 在另一个对象中,这对我来说仍然不是问题。

如果您有另一个更适合用法的示例,请告诉我。也许特定语言并没有从中受益那么多? (Javascript、PHP、ASP?)

【问题讨论】:

    标签: events event-handling event-listener


    【解决方案1】:

    您可能无法始终控制执行调用的代码。或者即使您是,您也不想在该代码中引入依赖关系。在这种情况下,最好让代码触发事件并允许您控制的代码或应该具有依赖关系的代码侦听事件并采取相应措施。

    例如,也许您正在创建一个可供其他人使用的库。他们没有源代码,或者以某种方式不能/不应该能够修改它(或者不应该)。您的文档指出在特定情况下会引发特定事件。然后,他们可以反过来响应这些事件。

    或者您的企业中可能有一些域库。您确实可以控制它们并可以修改它们,但从架构上讲,它们通常被认为是按照当前的编码方式工作的,不应更改。 (不想招致一轮 QA 来重新验证更新的代码,该代码属于另一个部门,他们不希望您更改它,等等)而且您处于您想要的位置代码能够在不同的情况/环境下做不同的事情。如果该代码引发了相关的事件,您可以将您的代码挂接到其中(和/或相应地换出),而不必弄乱该代码。

    只是几个简单的例子,我相信其他人还有更多。

    【讨论】:

    • 我可以控制的域库背后的想法让我很好地理解了,因为我使用的大部分东西我都可以完全控制。因此,既然我确实可以控制它,我需要问相反的问题,是否有理由不使用事件侦听器而不是函数调用?
    • @Organiccat:和以前一样,我确信还有其他原因。但对我来说最大的原因是依赖倒置原则。响应事件的代码可能具有(甚至是)逻辑上不属于引发事件的代码的依赖项。
    • 我后来想到了它,但我认为代码的重复也是使用函数而不是事件监听器的一个很好的理由(尤其是在不编写库时)。
    【解决方案2】:

    我的问题是,为什么在调用函数时创建一个事件监听器就可以正常工作?

    如果不知道要调用什么函数怎么办?

    举个经典的例子,一个用户可以点击的按钮。编写该库的人不知道单击按钮时要调用什么函数。如果每个 Button 只能在单击时调用相同的函数,那也是相当令人望而却步的。

    因此,您可以将事件处理程序附加到事件。然后当事件被触发时,Button 可以做它需要做的事情,而不必在编译时确切地知道它应该调用什么函数。

    【讨论】:

    • 这里没有任何解释。语言(例如 JS)有内置事件,它们很有用,但如果我创建的不是库,我看不出有任何理由自己创建事件。我可以在按钮上使用onclick 事件并调用我想要的尽可能多的函数这里有什么问题?我在这里是因为我找到了一些关于 NodeJS 事件的信息,但我不明白为什么我还要知道它们。
    • 您不是在应用程序中创建模块或消耗性组件吗?如果是这样的话,那将是非常强大的意大利面条代码。除了你可以很好地创建一个库之外,如果你发现它在某些 javascript 对象中很有用,为什么你不能制作有用的库的想法是牵强附会的?
    【解决方案3】:

    简而言之,您可以编写没有事件侦听器的代码,但使用事件侦听器可以帮助其他人使用与库相同的代码。

    【讨论】:

    • 您能否详细说明将其编码为库时将如何帮助他人?
    【解决方案4】:

    即使有了上面的详细答案,我仍然无法理解使用控制器/函数或事件侦听器之间的实际区别。

    所有这些答案中遗漏的一件事是,当您不想将代码如此紧密地耦合时,使用事件和 事件侦听器会派上用场。每个函数、类等都应该有单一的目的。

    假设您受到来自外人的 API 请求的打击。就我而言,我理解这个概念的确切问题是当我从 Stripe Webhooks 接收 API 调用时。

    Stripe Webhooks 的目的是:假设客户在您的网站上花费了 10,000 美元。您的标准程序是 Auth 和 Capture。更新数据库以反映他们的新会员状态。在一个完美的世界里,在我们公司的情况下,999/1000 次,这很完美。他们的卡要么被当场拒绝,要么付款通过。在这两种情况下,我们都会向他们发送一封电子邮件,让他们知道。

    但是当用户付款并且 Stripe 返回 Card Failure 错误(可能是许多不同的事情)的 1/1000 时间呢?在我们的案例中,我们向他们发送电子邮件并告诉他们计费失败。我们遇到的问题是,一些银行正在调查大笔收费,这作为错误返回,但几分钟后银行授权收费并捕获付款。

    那么有什么办法呢?输入 Stripe Webhook。如果发生这种情况,Stripe Webhooks 将访问 API 端点。实际上,每当付款未立即进行身份验证、捕获或客户要求退款时,Stripe Webhooks 都可以访问您的 API。

    这就是事件监听器派上用场的地方。 Stripe 使用客户信息和 Webhook 类型在 POST 上进行拍摄。我们现在将处理它,更新数据库,并向他们发送一封成功的电子邮件。

    但为什么不直接使用标准路由和控制器呢? 我们不只是使用标准路由和控制器的原因是因为我们要么需要修改已经定义的函数、类等,要么创建一系列耦合在一起的新类,例如 -> Stripe API Calls Received ,更新数据库,发送电子邮件。我们不是将它们紧密耦合在一起,而是使用事件侦听器首先接受 API 调用,然后点击其中的每个类、函数等,使所有内容都分离。

    我到处寻找,我认为 Laravel 文档解释得最好。给出一个具体的例子,我终于明白了,事件监听器的目的是什么:

    事件是解耦应用程序各个方面的好方法,因为单个事件可以有多个互不依赖的侦听器。例如,您可能希望在每次订单发货时向您的用户发送 Slack 通知。您可以引发 OrderShipped 事件,而不是将订单处理代码与 Slack 通知代码耦合,侦听器可以接收该事件并将其转换为 Slack 通知。

    https://laravel.com/docs/5.6/events

    【讨论】:

      【解决方案5】:

      我认为事件与函数调用的主要原因是事件是“侦听”,而调用是“进行”。因此,总是对另一个对象进行函数调用,而侦听器“选择”来侦听要从您的对象广播的事件。 观察者模式是对这种能力的一个很好的研究。下面是一个简短的 node.js 示例来说明这个概念:

      var events = require('events');
      var Person = function(pname) {
          var name = pname;
      };
      
      
      var james = new Person('james');
      var mary = new Person('mary');
      var loudmouth = new Person('blabberer');
      
      loudmouth.mouth = new events.EventEmitter();
      
      //jame's observer.
      james.read_lips = function(msg){
          console.log("james found out: " + msg);
      };
      
      //james adds his event to the emitter's event listener.
      james.enter_elevator = function(){
          console.log('james is in the elevator');
          //NOTE: james adds HIMSELF as a listener for the events that may
          //transpire while he is in the elevator.
          loudmouth.mouth.on('elevator gossip', james.read_lips)
      };
      
      //james removes his event from the emitter when he leaves the elevator.
      james.leave_elevator = function(){
          // read lips is how james responds to the event.
          loudmouth.mouth.removeListener('elevator gossip', james.read_lips);
          console.log('james has left the elevator');
      };
      
      //mary's observer
      mary.overhear = function(msg){
          console.log("mary heard: " + msg);
      };
      
      //mary adds her observer event to the emitter's event listeners
      mary.enter_elevator = function(){
          // overhear is how mary responds to the event.
          console.log('mary is in the elevator');
          //NOTE: now mary adds HERSELF to the listeners in the elevator and 
          //she observes using a different method than james which suits her.
          loudmouth.mouth.on('elevator gossip', mary.overhear);
      };
      
      loudmouth.speaks = function(what_is_said){
          console.log('loudmouth: ' + what_is_said);
          this.mouth.emit('elevator gossip', what_is_said);
      };
      
      james.enter_elevator();
      mary.enter_elevator();
      loudmouth.speaks('boss is having an affair');
      james.leave_elevator();
      loudmouth.speaks('just kidding');
      console.log('james did not hear the last line because he was not listening anymore =)');
      

      所以在这个“故事”中,演员选择倾听或何时不倾听来自第三方的事件。我希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 2013-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-23
        • 2021-04-22
        • 1970-01-01
        • 2020-12-23
        相关资源
        最近更新 更多