反应式编程与事件驱动编程有何不同?

时间:2015-12-28 14:11:07

标签: javascript reactive-programming frp bacon.js event-driven

我正在学习JavaScript中的反应式编程和功能反应式编程。我很困惑。

维基百科说,有多种方法可以编写反应性代码,例如命令式,OORP和功能性。我想知道事件驱动是否只是编写反应代码的另一种方式?

响应式编程如何与Promises相关?我认为承诺是事件驱动和回调地狱的替代方案。

5 个答案:

答案 0 :(得分:63)

  

反应式编程与事件驱动编程有何不同?

事件驱动的编程围绕着所谓的事件,这些事件是在事情发生时编程“触发”的抽象事物。代码中的其他位置“监听”事件并响应事件发生时他们需要做的事情。例如,事件可以是“用户按下此按钮”或“打印机已完成打印文档”。

反应式编程处理数据。最终,这是事件驱动编程的特例。事件:数据已更改。事件处理程序:更改一些更多数据(如果适用)。当您想到电子表格时,通常会清除此概念。如果您设置cell1 = cell2 + cell3,则会在cell2cell3的数据更改事件上隐式设置两个事件处理程序,以更新cell1的数据。 cell1的数据没有这样的事件处理程序,因为没有单元格依赖于它的值。

TL; DR;

  

维基百科说,有多种方法可以编写反应性代码,例如命令式,OORP和功能性。我想知道事件驱动是否只是编写反应代码的另一种方式?

事件驱动编程的想法与命令式与OO与功能性的概念正交。

  • Imperitive programming:专注于改变你的程序状态将实现你想要的。大多数计算机都是必不可少的(而不是declarative programming),而更高级别的语言有时是声明性的。相反,声明性编程处理编写代码,指定您希望它做什么,而不是您希望代码如何执行。
  • Object Oriented programming:处理所谓的对象或带有相关方法的数据包。与函数式编程不同,因为这些方法能够访问与对象关联的数据。
  • Functional programming:处理可重复使用的功能,或处理输入和输出的程序。这与OO编程不同,因为传统上函数不能将数据与输入和输出以外的函数相关联。

Event driven programming:构建程序以处理(“处理”)程序中发生的其他事件(“事件”)。换句话说,它在逻辑上构建您的代码

When Event1 happens
    do A and B

When Event2 happens
    do B and C

但是有很多方法可以编写这段代码,事实上有许多方法可以强制编写代码,有很多方法可以在函数上编写代码等等。不过这里有一些例子。

势必(使用事件循环):

while(true)
    // some other code that you need to do...

    if Event1 then
        do A
        do B
    if Event2 then
        do B
        do C

面向对象(使用后台线程):

// event queue
events = new EventQueue()

handler = new EventHandler()
// creates background thread
Thread.DoInBackground(handler.listenForEvents(events))

// ... other code ...

// fire an event!
events.enqueue(new Event1())

// other file
class EventHandler
    Func listenForEvents(events)
        while(true)
            while events.count > 0
                newEvent = event.dequeue()
                this.handleEvent(newEvent)
            Thread.Sleep(Time.Seconds(1))

    Func handleEvent(event)
        if event is Event1
            this.A()
            this.B()
        if event is Event2
            this.B()
            this.C()

    Func A()
        // do stuff
        return

    Func B()
        // do stuff
        return

    Func C()
        // do stuff
        return

功能性(对事件的语言支持)

on Event(1) do Event1Handler()
on Event(2) do Event2Handler()

Func Event1Handler()
    do A()
    do B()

Func Event2Handler()
    do B()
    do C()

Func A()
    // do stuff
    return

Func B()
    // do stuff
    return

Func C()
    // do stuff
    return

// ... some other code ...

// fire! ... some languages support features like this, and others have
// libraries with APIs that look a lot like this.
fire Event(1)
  

反应式编程与Promises有何关系?

Promise是程序执行流程的抽象,可以总结如下:

  • Asker:每当你做完你正在做的事情时,你会给我回电话吗?
  • 回答:当然,我承诺

这里没什么特别的,除了它是另一种思考代码执行顺序的方法。例如,当您调用远程计算机时,promise很有用。有了承诺,你可以说“当你从这个远程呼叫返回时给我回电话!”。无论您使用哪个库,承诺都会在从远程计算机返回某些内容时给您回电。通常,这很有用,因为它允许您在此期间执行其他操作而无需等待返回的调用。

Punch line:有许多不同风格的代码,但它们在事件驱动和反应式编程的模式中不起太大作用。据我所知,您可以使用大多数语言进行事件驱动和/或反应式编程。

答案 1 :(得分:20)

  

响应式编程如何与Promises相关?我认为这个承诺是事件驱动和回调地狱的替代方案。

在实践中,两者是相关的,我喜欢称Promises为功能性反应式编程的门户药物。

+----------------------+--------+-------------+
|                      |  Sync  |    Async    |
+----------------------+--------+-------------+
| Single value or null | Option | Promise     |
| Multiple values      | List   | EventStream |
+----------------------+--------+-------------+

Promise可以被认为是带有一个项目的EventStreams,或者你可以将EventStreams视为多个Promise随着时间推移。

Promise可以链接,接近反应式编程:

getUser() // return promise
   .then((userId) => {
       return fetch("/users/"+userId)
   })
   .then((user) => {
       alert("Fetched user: " + user.name)
   })

与bacon.js相同:

const userStream = userIdStream // EventStream of userIds
   .flatMapLatest((userId) => {
       return Bacon.fromPromise(fetch("/users/"+userId))
   })
const userNameStream = userStream.map((user) => user.name)
userNameStream.onValue((user) => {
   alert("Fetched user: " + user.name)
})

两个代码片段都做同样的事情,但思维方面存在很大差异:使用promises,你正在思考以明确的方式处理单个动作和异步步骤 - 思维是必要的,你是一步一步做的事情。使用FRP,您可以通过应用这两个转换步骤"从userIds流创建一个用户名流。如果您有一个用户名流,而不关心他们来自何处,并说出"每当有新用户名时,请将其显示给用户"。

FRP编码样式将指导您将问题建模为值流(即随时间变化的值)以及这些值之间的关系。如果您已经了解Promises,那么初始学习曲线会更容易一些,但只有当您开始思考并以不同方式对问题建模时才能获得主要好处 - 使用FRP库进行命令式编程是可能的(如果不是非常有用)。

答案 2 :(得分:3)

差异主要与如何“配置”(或声明)事物约定有关:事物发生时发生什么。

在反应式编程中,您声明一个反应以进行更改。您不必预先预料到该更改所需的反应,您可以在以后随时添加-声明-该反应。因此,可以将其视为“拉动”或“监视”策略。

因此,在反应编程中,您连接到/ 观看您知道存在的数据。数据在这里至关重要。

示例:用户点击页面上的一个项目->更新计数器用户点击次数。

示例计算器应用程序:计算器显示绑定到所有按钮,并且对任何更改(单击按钮)做出反应,并在显示屏上进行自己的更改。按钮不知道其单击可以被其他任何部分利用。

在事件驱动的编程中,您以命令式编写的代码在特定情境中触发事件。您需要在此处进行明确的预先说明,因为该事件需要先触发才能稍后被接收-因为基本上您是在代码的“发生更改”部分中 push 事件。因此,这是一种“推动”策略。

因此,在事件驱动的编程中,您在特定情况下推送可能会发生 的事件被其他代码部分接收。这里的情况很重要,数据无所谓。

示例:有人访问了联系页面->触发了一个事件(任何侦听器可能最终都不会收到该事件,这是许多模块和库的典型情况)。 / p>

示例计算器应用程序:计算器显示屏只是一个侦听器,按钮触发事件。按钮需要知道它们存在于特定的上下文中(但是-由于事件监听器模式-不必知道该上下文到底是什么),因此需要它们来触发事件。

因此,在大多数情况下,它们只是不同的约定。 看这个简单的例子。命令式方法示例:

event: perform some operation on a, e.g. a += value, and trigger the event
listener: counter++

反应式声明方法示例:

counter: whenever an operation on a occurs, react with this: counter++

在最后一个示例中,无需触发任何内容-您只需通过反应来“钩住”可能发生的任何事情。

那么,您可能会说,反应是通过反应性方法绑定到a的,而在命令性事件驱动的方法中,您推送一个事件,该事件以后可以被侦听器接收-并且由于这种类型的方法是与数据没有任何关系,您可以稍后将其更改:a += value,甚至完全删除a。事件驱动方法本质上与数据无关。

因此,如您所见,反应式编程是面向数据的(数据的更改会触发其他代码而作出反应),而事件驱动的编程是面向过程的(是否更改以及更改什么数据,如果有的话,您都可以)只需触发一个事件,该事件将被其他代码部分接收)。在后一种情况下,您需要知道,这需要“通知”代码的其他部分,然后您必须预见应该触发该事件。在前一种情况下,您不必执行此操作,您可以随时执行或根本不执行此操作-不需要触发事件-但是这里的窍门是必须有一个可以与之联系的“东西”您的反应声明,一种观察者,可让您对观察到的更改做出反应。

答案 3 :(得分:0)

对我而言,这就像比较橙子和苹果。让我们尝试以简单的方式定义什么是什么,从而区分事物:

反应式编程是一种编程范例,当人们希望实现类似于KnockoutJS等库中的数据绑定的功能时,应用该编程范例。另外一个例子是Excel公式:所有单元格都像内存中的变量。有些只是简单地保存一些数据和那些从该数据计算出来的数据。如果前者改变,后者也改变。注意范式是关于较低级别的实现;当有人谈论反应式编程时,他们指的是数据,它的变化以及变异时会发生什么。

另一方面,事件驱动编程与系统架构有关。根据范式事件和事件处理程序是系统的基础,一切都建立在它们周围。常见的例子是UI和Web服务器多路复用。你觉得这一切有什么不同吗?范例适用于整个系统或子系统的级别。

  

响应式编程如何与Promises相关?我认为承诺是   事件驱动和回调地狱的替代方案。

Promise是实现并发和特定执行顺序的工具。它可以用在任何范例中。

在实践中,范例用于不同的目的和不同的层次。您可以使用一些反应性代码进行事件驱动设计。您可以使用使用反应式设计模式的分布式系统。然而,事件最终是更高层次的概念。 Reactive是关于数据及其重新评估,实现方法或其细节,事件自然来自案例并驱动您的设计。

答案 4 :(得分:0)

反应性编程完全是关于流,它可以是事件流或其他任何东西。它正在发布/通知这些流或订阅/观看这些流或流转换导致某些事件。因此两种编程范式都是相关的。