事件处理程序和回调之间的区别

事件处理程序和回调函数之间的区别是什么?

54883 次浏览

事件处理程序是一种回调类型。每当事件发生时就会调用它。这个术语通常用于用户界面,其中的事件包括移动鼠标、单击某些东西等等。

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

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

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

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

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

虽然“事件处理程序”模式更加复杂,但它更加健壮,在操作失败时挂起的可能性更小。它也使得 GUI 的响应性更强。

回调(来自 Wikipedia) : “作为参数传递给其他代码的可执行代码”。
事件处理程序(同样来自 Wikipedia) : “处理程序中接收的输入的异步回调子例程”。

这正是我一直理解的方式: 事件处理程序是一种非常特定的回调类型。

事件处理程序也是回调函数,只不过它的解耦性更强。

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

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

如果是这样,我们在变量中存储一个值“ Success”,如果不是,那么我们在变量中存储“ false”。该变量由程序“ P”中的函数“ checkIfAPICallSucced”更新,该函数接受 API 响应作为参数来决定该变量的命运。

下面是一个正常的回调场景将如何实现它:

  • 步骤1: P 进行 API 调用
    • 注意1.1: API 调用本身由一个单独的进程“ Q”(比如一个网络程序)处理
    • 注意1.2: 假设 P 等待(阻塞或休眠)直到 Q 得到结果
    • 注意1.3: 所以 Q 需要让 P 知道响应何时到达
    • 注意1.4: 因此,在调用 API 时,P 需要将指向函数“ checkIfAPICallSucced”(回调)的指针移交给 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 的是同一个团队(高信任级别)时,简单的回调可以节省资源。

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

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

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

因此,被调用方(somLibrary.getMeSomething)被赋予了一个匿名回调函数,不久之后,这个函数被 returnedData 调用。回调类似于对单个接收者的单发事件。

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

因此,在 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'

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

当一个事件发生时,你会被告知发生了一些事情。当使用回调时,您将被要求参与某些事情。

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

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

事件 -考虑服务器(员工)和客户端(老板)。一个员工可以有多个老板。员工提出事件,当他完成任务,老板可能决定是否听从员工事件。雇员是出版商,老板是订阅者。

回调 -老板特别要求员工完成一项任务,在任务完成后,老板希望得到通知。员工将确保当任务完成时,他只通知要求的老板,而不是所有的老板。如果部分工作已经完成,员工将不会通知老板。只有在所有的任务都完成之后。只有一个老板要求提供信息,而员工只向一个老板发布了回复。

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

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

回调是要求另一个函数执行业务操作并发送结果,而事件不是要求切换控制,因此我们只能处理业务操作。

例如: 按钮点击是事件(我们正在做业务操作的按钮点击)。在回调中循环集合,因为 forEvery 方法正在执行业务操作并提供结果。

一个事件具有特殊的特征,如起泡等。如果您不需要这些特性,那么只需使用回调即可。

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

在交互式计算机图形学中,这些术语的使用方式与其他领域略有不同。例如,如果我们使用类似于(比如说 OpenGL)的图形 API,那么 活动就是窗口上的不同操作。操作包括窗口大小调整、鼠标移动、键盘交互等。没有必要事件总是需要涉及到物理,它也可以是 逻辑事件等程序员可以旋转和移动的位置。

回调(又名事件处理程序) 是处理偶数模式输入的函数

这里 myMouse是一个逻辑事件,glutMouseFunc()处理的是回调。

另外需要注意的一点(也许是最重要的一点)是,事件可以由事件的许多使用者/接收者处理。

虽然回调意味着通知的单个接收者(好吧,除非你是在 JavaScript 世界里,猴子修补函数是一种常态...)。

因此,回调用于两方之间更多的“私有”通信,而事件用于更多的“公共公告”和多个订阅者。

另外,正如在另一个答案中提到的(参见 supi的答案) ,事件中存在更好的解耦。而且更灵活(例如,您通常可以随时删除事件处理程序,而使用回调有时是不可能的)。