为什么将我的FRP实现更改为响应式会出现延迟?

5
我已经使用threepenny-gui库成功编写了贪吃蛇游戏的版本,但我不喜欢需要手动调用newEventaddStateUpdate,而希望完全基于事件定义行为。例如这样:
(updates, addUpdate) <- liftIO newEvent
managerB <- accumB initialManager updates

on UI.tick timer $ \_ -> addUpdate $ \manager -> manager'

相较于:

managerB <- accumB initialManager $
  UI.tick timer $> \manager -> manager'

据我所知,第二种方法更符合FRP的惯用法,因为它使用实际事件定义行为,而不是创建代理事件来进行更新。但是当我进行此更改时,会出现以下两个问题之一:
  1. 如果我首先定义managerB(使用RecursiveDo访问下面定义的timer),则根本不会呈现任何内容。
  2. 如果我将managerB移到最后(使用RecursiveDo从DOM元素访问managerB),则第一次按箭头键时的初始移动会延迟,并且帧以颤抖的方式呈现。
我有做错什么吗? 我应该如何构造这些事件/行为以符合惯用方式?
代码差异在这里:https://github.com/brandonchinn178/snake/compare/inline-event-handlers
1个回答

0
一个不是特别漂亮的解决方法,我在您的代码库中的另一个分支上测试过,可以说是同时使用两种方法:重新触发tick事件并使用它来定义managerB,而不是使用UI.tick定时器
  (timeE, fireTime) <- liftIO newEvent
  on UI.tick timer $ \_ -> liftIO (fireTime ())

  let managerUpdateE =
        fmap concatenate . unions $
          [ timeE $> getNextManagerState
          --  Instead of: UI.tick timer $> getNextManagerState
          --  etc.

问题似乎在于将UI.tick timer直接插入事件网络会妨碍Threepenny发送所需的JavaScript调用以及及时更新UI。使用onfireTime的间接方式(特别是,timeE应该在UI.tick timer之后发生)似乎可以绕过这个问题。一个不那么侵入性的解决方法是,在UI.tick timer的处理程序中显式调用flushCallBuffer;然而,在我的测试中,这样做可以大大减少抖动,但并不能完全消除它。(有关可能相关的背景信息,请参见threepenny-gui issue #191。)
关于第一个按键的延迟问题,看起来可以通过将你对UI.start timer的调用移到gui的最后,即在managerB和你的事件网络设置完成之后,来消除它。
(另外,根据Graphics.UI.Threepenny.Timer文档的建议,在编译可执行文件时为ghc-options设置-threaded可能是一个不错的主意,即使这似乎对你在这里描述的问题没有影响。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接