Reflex-FRP事件触发疑问:logC未触发及输出顺序异常
Haskell Reflex-FRP:事件触发与输出顺序疑惑
这是我首个Haskell与Reflex-FRP项目,参考GitHub上的示例与项目搭建。我尝试向控制台记录三个事件,但仅有两个触发,同时对输出顺序存在疑惑。
相关代码
路由函数
route :: R FrontendRoute -> State -> FTNet t m (Event t State) route (FrontendRoute_Logout :/ ()) (StateLoggedIn _) = do pb <- getPostBuild el "h1" $ text "logging out..." setRoute $ FrontendRoute_Login :/ () <$ traceEvent "logA" pb setRoute $ FrontendRoute_Main :/ () <$ traceEvent "logB" pb pure $ StateLoggedOut <$ traceEvent "logC" pb
类型定义
data State = StateLoggedOut | StateLoggedIn User deriving stock (Eq, Show) type FTNet t m = RequesterT t AuthApi (Either Text) m data AuthApi a where …
控制台输出
logB: () logA: ()
困惑点
logC为何未被打印?- 输出顺序由何决定,为何
logB先于logA输出?
问题解答
1. logC未打印的原因
Reflex的事件遵循**"无订阅则无激活"**的规则:只有当事件被某个消费逻辑(比如状态更新、UI响应、performEvent系列函数)订阅时,事件流才会被实际执行。你返回的StateLoggedOut <$ traceEvent "logC" pb事件,当前代码里没有任何地方去消费它——既没有绑定到状态更新逻辑,也没有显式订阅,因此traceEvent的日志不会触发。
要让logC输出,需要确保事件被使用。比如在函数内显式订阅:
route (FrontendRoute_Logout :/ ()) (StateLoggedIn _) = do pb <- getPostBuild el "h1" $ text "logging out..." setRoute $ FrontendRoute_Login :/ () <$ traceEvent "logA" pb setRoute $ FrontendRoute_Main :/ () <$ traceEvent "logB" pb let stateEvent = StateLoggedOut <$ traceEvent "logC" pb performEvent_ $ void stateEvent -- 显式订阅事件,触发日志 pure stateEvent
2. 输出顺序的问题
在同一个作用域内多次调用setRoute这类函数时,事件的执行顺序不取决于代码编写顺序,而是由Reflex内部的事件调度队列和setRoute的实现细节决定。你两次基于pb事件调用setRoute,这两个事件会被框架合并处理,但队列的执行顺序不受代码书写顺序约束,因此出现了logB先于logA输出的情况。
如果需要严格保证顺序,应该用sequenceEvents将事件合并为有序流:
route (FrontendRoute_Logout :/ ()) (StateLoggedIn _) = do pb <- getPostBuild el "h1" $ text "logging out..." -- 强制按顺序触发logA、logB let orderedEvents = sequenceEvents [ FrontendRoute_Login :/ () <$ traceEvent "logA" pb , FrontendRoute_Main :/ () <$ traceEvent "logB" pb ] setRoute $ switchDyn $ current orderedEvents let stateEvent = StateLoggedOut <$ traceEvent "logC" pb performEvent_ $ void stateEvent pure stateEvent
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

