比较core.async和功能反应式编程(+ Rx)

til*_*lda 59 asynchronous clojure frp reactive-programming core.async

在将Clojure的core.async与所谓的Reactive Extensions(Rx)和FRP进行比较时,我似乎有点困惑.他们似乎解决了类似的异步 - 长期问题,所以我想知道什么是主要差异,在什么情况下优先于另一个.有人可以解释一下吗?

编辑:为了鼓励更深入的答案,我想让问题更具体:

  1. Core.async允许我编写同步代码.但是据我所知,FRP只需要一级嵌套回调(处理逻辑的所有函数都作为参数传递给FRP API).这似乎两种方法都不需要回调金字塔.确实,在JS中我必须写function() {...}多次,但主要问题,嵌套回调,也在FRP中消失了.我做对了吗?

  2. " FRP通过控制流完善了信息的沟通"你能(某人)请给出更具体的解释吗?

  3. 我不能像通过频道那样绕过FRP的可观察端点吗?

总的来说,我理解历史上两种方法的来源,我在这两种方法中都尝试了很少的教程.然而,我似乎被差异的非显而易见性"瘫痪"了.是否有一些代码难以在其中一种中编写而使用另一种代码很容易?那架构原因是什么?

Gui*_*ler 31

我认为主要的问题是你对解决问题的假设并不是这样,因为他们都没有解决异步性 "问题".

抽象

FRP主要思想是传播变化,考虑完成Excel所做的事情,在级联中依赖于彼此定义细胞,当一个细胞发生变化时,重新计算级联中的所有依赖细胞.

core.async主要的想法是系统分解,认为queue在不同的过程中使用a分离关注点,core.async如果不是队列,你有渠道,但你得到了想法.

因此,删除金字塔形代码不是任何一种技术的目标,它们在不同的抽象层上运行.

关于收集控制流程

关于收集通信和流量控制的想法取自原始的核心异步帖子.

虽然有各种机制可以使事件/回调更清晰(FRP,Rx/Observables),但它们并没有改变它们的基本性质,即在一个事件上运行任意数量的其他代码,可能在同一个线程上,导致诸如"不要在你的处理程序中做太多工作"的警告,以及像"回调地狱"这样的短语.

重新定义,如果在事件处理程序中有业务域代码,则已将X事件处理X发生时的操作进行了比较.

这就是core.async铲球,因为引入队列/通道的中间,有助于为中分类.

履行

关于回调和将可观察端点作为参数传递的所有问题都只是实现问题,它实际上取决于Rx实现和API.

如果你看一下React可重用的组件,你真的没有太多的回调地狱可见,你就会想到传递可观察的东西.

最后的想法

即使Rx可以用于建模任何数据流,更常用于UI渲染 a-la Excel,以简化模型更改时更新视图的方式.

另一方面,Core.Async当任何两个子系统相互通信(与队列相同的使用场景)时,可以用来模拟关注点的分离,在UI渲染链上使用它的主要思想是分离:

  • 服务器端事件生成和处理
  • 该事件如何影响您的模型

因此,您可以拥有core.asyncFRP在一起,因为core.async它将分离关注点,并FRP在您的模型更新后定义级联数据流.


osk*_*rkv 23

至少有一个主要的主要差异,以及我认为,"[FRP]通过控制流完成信息传播"的Rich,如下所示.

回调是执行的代码.并且该执行必须在某个时间点在某个线程中发生.通常,时间是事件发生的时间,而线程是注意/产生事件的线程.如果生产者改为在频道上放置消息,您可以随意使用消息,无论您想要什么线程.因此,回调本质上是一种通信形式,通过指示回调代码执行的时间和地点,将通信与控制流程进行比较.如果由于某种原因必须使用回调,只需使用它们在队列/通道上放置一条消息就可以恢复正常.

因为core.async不那么复杂,所以只要没有充分的理由使用替代方案,它应该是首选的.

  • Rx框架具有简单的API(即调度程序),用于声明何时以及应该执行哪些线程代码.它不必是通知/产生事件的线程. (2认同)
  • @erikprice好的!我实际上没有使用Rx,只是快速阅读它.我仍然认为我所谈论的是Rich的意思. (2认同)

noi*_*ith 11

Clojure core.async是Go语言的go块的一个端口.基本概念是表示线程之间异步通信的通道.

目标是能够编写正常的顺序代码块,访问通道以获取输入,并将其无缝转换为状态机,为您计算CSP转换.

与FRP形成鲜明对比,FRP仍然基本上是关于回调,它通过控制流完成了消息的通信.core.async完全消除了代码中的回调,并将控制流与消息传输分开.此外,在FRP中,通道不是第一类对象(即,您不能将FRP通道作为FRP通道上的值发送).

  • FRP并非从根本上讲是回调.回调只是更高阶结束的手段.如果它只是关于回调,我们称之为普通的反应式编程. (5认同)
  • 另外,我不知道任何FRP库禁止你从另一个observable发送一个observable(你的话中的"FRP频道"). (4认同)
  • "此外,在FRP中,通道不是第一类对象(即,您不能将FRP通道作为FRP通道上的值发送"由于Rx框架中没有"通道",我假设您的意思是可观察/信号哪个不正确 - 您当然可以将观察信号/信号作为来自其他可观测量/信号的值发送. (4认同)
  • 与@ christopher-harris一致认为它不仅仅是关于回调,因为你可以在没有它的情况下获得很好的组合行为,例如distinctUntilChanged,retry,sample,throttle等.有许多运算符根本不需要回调.它更多的是链接而不是其他任何东西. (3认同)

Mat*_*cki 5

编辑:

回答你的问题:

  1. 是的,很多时候,可以消除回调,例如使用重试逻辑,distinctUntilChanged以及许多其他内容,例如:

    var obs = getJSON('story.json').retry(3);

    var obs = Rx.Observable.fromEvent(document, 'keyup').distinctUntilChanged();

  2. 我不确定这与流量控制使其更复杂有关.

  3. 是的,你可以传递像通道一样的对象,就像下面的对象一样.

例如,您可以使用RxJS和ReplaySubject在此处拥有类似通道的行为:

var channel = new Rx.ReplaySubject();

// Send three observables down the chain to subscribe to
channel.onNext(Rx.Observable.timer(0, 250).map(function () { return 1; }));
channel.onNext(Rx.Observable.timer(0, 1000).map(function () { return 2; }));
channel.onNext(Rx.Observable.timer(0, 1500).map(function () { return 3; }));

// Now pass the channel around anywhere!
processChannel(channel);
Run Code Online (Sandbox Code Playgroud)

为了让多一点具体的,比较从大卫·诺伦的职位代码这里有RxJS FRP例子在这里