【问题标题】:Difference between event handlers and callbacks事件处理程序和回调之间的区别
【发布时间】:2010-01-15 05:57:09
【问题描述】:

事件处理程序和回调函数有什么区别?

【问题讨论】:

标签: architecture callback event-handling language-agnostic


【解决方案1】:

回调是作为参数传递给另一个过程的过程。接收参数的过程可以调用它,或者共享它以便系统中的一些其他过程可以调用它。

事件处理程序是在事件发生时调用的过程。可以是回调。

【讨论】:

    【解决方案2】:

    一般来说,“回调”是在检测过程的控制之下。 因此,您告诉 GUI 管理器“按下此按钮时调用 myaction”,然后 GUI 管理器会在按下按钮时调用该操作。

    另一方面,事件处理程序在一个步骤中运行。 GUI 管理器被配置为向事件处理程序发送消息。您告诉事件管理器按钮按下由myaction 程序处理。当按下按钮时,GUI 管理器将消息放在事件处理程序的队列中并继续进行 GUI 管理。事件处理程序从队列中获取消息,看到它是一个按钮按下,启动myaction 程序,然后继续处理下一个事件。通常myaction程序会作为独立线程甚至独立进程运行。

    虽然“事件处理程序”模式更复杂,但它更加健壮,并且在操作失败时挂起的可能性更小。它还提供了响应更快的 GUI。

    【讨论】:

      【解决方案3】:

      事件处理程序是一种回调。每当发生事件时都会调用它。该术语通常用于用户界面,其中事件是诸如移动鼠标、单击某物等。

      【讨论】:

      • 那么还有其他场景可以调用回调吗?除了活动?你能举一个这样的例子吗?
      • @cletus,我喜欢你的回答,但你能否添加一些关于什么是事件处理程序和什么是回调的示例。我理解你所说的.. 但是回调总是发生,因为“发生了一些事情”,发生了 99,999999% 的事情(我谦虚地承认)是一个事件,我不会使用其他词,因此,其他含义。 ..之前得出结论,从技术上讲是一样的。请帮助我提高我的认知度。谢谢!!问候! :)。试着这样想……你能列举出不属于特定事件的其他类型的回调吗?
      【解决方案4】:

      事件 - 想想服务器(员工)和客户端(老板)。一个员工可以有多个老板。员工发起事件,当他完成任务时,老板可以决定是否监听员工事件。员工是发布者,老板是订阅者。

      回调 - 老板特别要求员工执行一项任务,在任务完成时,老板希望得到通知。员工将确保当任务完成后,他只通知请求的老板,而不是所有老板。如果部分工作完成,员工不会通知老板。完成所有任务后才会有。只有一位老板要求信息,员工只回复一位老板。

      【讨论】:

        【解决方案5】:

        回调(来自维基百科):“作为参数传递给其他代码的可执行代码”。
        事件处理程序(再次来自维基百科):“异步处理程序中接收到的输入的回调子程序”。

        这恰好是我一直以来的理解方式:事件处理程序是一种非常特殊的回调类型。

        【讨论】:

          【解决方案6】:

          这个问题很老了,但我发现MSDN 的这个链接很有趣。我希望任何偶然发现这个问题的人都能从这个链接中得到一些东西。

          【讨论】:

          • 有趣。套用这篇文章:一个事件就像一条推文;任何人都可以阅读和回复。而回调就像一条短信;只有您发送给的人才能阅读和回复。
          【解决方案7】:

          另一方面,事件描述了过去发生的事情,而回调通常在事情发生时使用。

          当事件触发时,您会被告知发生了什么事。使用回调时,系统会要求您参与某些事情。

          库或框架可能会发出事件,让您知道发生了什么事。框架为您提供了可以插入代码(可能作为回调)的点,以便您可以积极参与流程。

          部分问题在于事件、回调指的是技术机制以及更抽象的过程。

          【讨论】:

            【解决方案8】:

            底层机制相似,但语义不同。回调和事件处理程序都被异步调用

            回调函数通常从调用例程显式传递以请求某些信息。信息在一段时间后返回,由被调用者作为参数传递回回调。此时,调用例程完成其业务。通常回调是​​一个闭包 - 在调用例程中的语法上,并且通常是未命名的(匿名的)。它可能看起来有点像下面的,在 javascript 中:

            function caller() {
                someLibrary.getMeSomething(arg1, arg2, function(returnedData) {
                    // this is the callback which will do something with returnedData
                });
            }
            

            所以被调用者(someLibrary.getMeSomething)被赋予了一个匿名回调函数,一段时间后这个函数被调用返回数据。回调就像对单个接收器的单次事件。

            事件处理程序也被“回调”,但通常它们会在较长时间内用于多个事件,例如鼠标点击、网络事件等。此外,多个对象可能对同一事件感兴趣。由于这些原因,您通常“订阅”或“注册”设置代码中的事件(如对象初始化),并且事件处理程序更典型的是命名方法。通常,每个事件类型也被标识为常量或字符串。

            所以在 Python 中它可能看起来像:

            class MyUIClass:
            
                def __init__(self):
                    someUILib.register(someUILib.events.MOUSE_CLICK, self.my_mouse_click_handler);
            
                def my_mouse_click_handler(self, eventInfo):
                    # do something with event
                    if eventInfo.x < 100:
                         print 'You clicked in the margin'
            

            【讨论】:

            • 不需要异步调用回调。在最简单的形式中,回调只是作为参数传递给其他代码的函数/过程引用。其他代码可以随意同步调用回调函数。另外,lambda 表达式在定义事件处理程序代码中也很普遍,因此代码是在命名方法还是匿名方法中定义与定义某事物是“回调”还是“事件处理程序”无关。主要区别在于如何回调代码的模式,而不是如何定义代码。
            【解决方案9】:

            回调是作为参数传递给另一个函数(方法)的函数(方法)。接收参数的函数(方法)可以调用它,或者共享它以便系统中的其他函数(方法)可以调用它。

            事件处理程序是在事件发生时调用的函数(方法)。可以是回调。

            【讨论】:

              【解决方案10】:

              事件处理程序也是一个回调。它只是更加解耦。

              传统上,进程“A”会将指向其中一个方法(回调)的指针传递给外部进程“B”,以便 B 稍后可以根据实现的逻辑调用它。

              例如假设我们的目标是检查 API 调用是否成功。

              如果成功,我们将值“成功”存储在变量中,如果没有,则将“失败”存储在其中。这个变量由我们的程序“P”中的一个函数“checkIfAPICallSucceded”更新,它接受 API 响应作为参数来决定这个变量的命运。

              以下是普通回调场景的实现方式:

              • 步骤 1:P 进行 API 调用
                • 注意 1.1:API 调用本身由单独的进程“Q”(比如网络程序)处理
                • 注 1.2:假设 P 等待(阻塞或休眠)直到 Q 得到结果
                • 注 1.3:所以 Q 需要让 P 知道响应何时到达
                • 注意 1.4:因此 P 在进行 API 调用时需要将指向函数“checkIfAPICallSucceded”(回调)的指针交给 Q
              • 第 2 步:P 将指针与调用一起传递给“checkIfAPICallSucceded”
              • 第 3 步:Q 收到 API 调用请求以及回调指针
              • 第 4 步:Q 接收来自 API 调用的响应并将其与指向 P 的指针一起传递
              • 第 5 步:P 以响应作为参数调用回调。

              这里的问题是 Q 对 P(P 中回调的内存地址)“了解太多”。这可能并不理想。

              在事件处理程序中,不同之处在于回调本身永远不会传递给 Q。相反,在收到响应时,Q 只是宣布一个事件(包含响应的状态和数据的对象)。另一个称为事件循环“EL”的程序(这是一个侦听事件的简单程序)管理回调的执行。

              • 步骤 1:P 对 Q 进行 API 调用
                • 注意 1.1:如上所述,事件处理程序模型还有另一个称为“EL”的程序或进程
                • 注 1.2:P 将事件处理程序(回调)传递给 EL 而不是 Q
                • 注意 1.3:EL 做了两件事 - 它维护一个包含指向已注册回调的指针的表,并且它还不断检查称为事件队列的内存结构中的事件对象
                • 注意 1.4:在对 Q 进行 API 调用并向 EL 注册事件处理程序后,P 不再等待并继续执行下一条语句
              • 第 2 步:收到响应后,Q 将事件“e”推送到事件队列中
              • 第 3 步:EL 然后在其下一个循环中拾取“e”
              • 第 4 步:EL 然后以 e 作为参数从表中调用相应的回调

              这样 Q 永远不会知道 P 的任何事情。事实上 Q 甚至可能不会直接将 e 推送到事件队列中,操作系统可能会代表它执行此操作,在这种情况下 Q 甚至不知道 EL。

              使用事件循环来处理回调会带来以下好处:

              • 一旦 P 从其当前执行任务中解放出来,就可以触发回调(这可以避免启动单独的线程和由此产生的线程管理问题)
              • 事件队列可以继续存储事件,避免阻塞 Q,否则 Q 需要阻塞直到第一个事件由 P 提供服务。

              因此,事件处理程序模型需要额外的程序 (EL),因此需要额外的资源(CPU 时间和内存)。它最适合 I/O 操作,几乎可以肯定这些程序是由完全不同的团队开发的(因此信任级别低)

              Q “知道” P 的简单回调可能听起来令人不快,但是当创建 Q 和 P 的同一团队(高信任度)时,简单的回调可以节省资源。

              【讨论】:

              • 我不认为回调的定义意味着异步,你也可以有同步回调。
              • @Lambage 完全同意。回调可以是同步的。这是一个非常古老的答案,为了更清楚,我刚刚再次更新。
              【解决方案11】:

              在最高抽象级别上,事件回调和处理程序是相同的,即您有一个事件和对该事件的响应。 (响应只是一段代码,需要运行以响应发生的事情。)两者之间的区别在于,如果直接或间接调用此代码。在回调的情况下,直接调用代码,即回调本身。在事件处理程序的情况下,首先将此事件传达或通知给正确的接收者,然后再调用代码。

              【讨论】:

                【解决方案12】:

                事件具有冒泡等特殊功能。如果您不需要这些功能,则只需使用回调即可。

                【讨论】:

                  【解决方案13】:

                  回调是要求另一个函数进行业务操作并发送结果,而事件不是要求移交控制权,以便我们只能处理业务操作。

                  例如:按钮点击是事件(我们在点击按钮时进行业务操作)。由于forEach方法在进行业务操作并提供结果,所以在回调中循环集合。

                  【讨论】:

                    【解决方案14】:

                    事件处理程序是来自系统的回调。

                    【讨论】:

                    • 它过于简单化了。但。您可以在没有任何事件的情况下自行调用回调函数,或者使用事件处理程序无法执行此操作。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-05-10
                    • 1970-01-01
                    • 2017-01-29
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多