大家好,这里有一个有点棘手的错误需要报告... 我们在使用 ClojureScript 上的 core.async 时遇到了一些问题。我们的应用围绕一个大型事件循环构建,该循环会阻塞在来自用户活动或 API 调用对应的一个或多达多个通道的消息上。问题似乎出在这个事件循环中 - 我们正在使用 alts! 从任何可用通道中提取消息,但有时日志显示我们会达到 alts! 但从未解除阻塞。然而,通过更多的日志记录,我可以看到列表中的一个通道有后续写入操作,因此我不太清楚发生了什么。
这就是高层次概述,现在谈一下代码。
我们主要的事件循环如下
(log "进入主事件循环。")
(go
(while true
(log "alts! 通道散列: " (map hash (:channels @app)))
(let [[message channel] (alts! (seq (:channels @app)))]
(log "alts! 解除阻塞,调用我们的 process-message"))
(swap! app process-message message channel)
(log "process-message 完成,循环"))))
{{process-message}} 是我们应用内部的一个函数,但我不认为它的细节是必要的。在 Safari 卡住的场景中,日志看起来像
[日志] process-message 完成,循环 (main.js,行 62)
[日志] alts! 通道散列: (16 12 19 33) (main.js,行 82)
[日志] Socket 已连接。 (socket.js,行 309)
[日志] 向散列 19 的通道 put! (socket.js,行 86)
[日志] 消息是 [:metronome [:staff [{:description nil, :deletable true, :email nil, :isAdmin true, :isTrainer false, :telephone nil, :name "Fynder Admin", :picture nil, :userId 1} {:description nil, :deletable fa... (socket.js,行 87)
[日志] put! 回调给出了 true (socket.js,行 89)
[调试] Metronome:staff 数据解码。 put! 完成:12.282ms (socket.js,行 93)
[日志] 向散列 19 的通道 put! (socket.js,行 86)
[日志] 消息是 [:metronome [:class-types [{:deletable false, :picture nil, :name "CycleCore", :id 2, :description "CycleCore 是一个 55 分钟的双工锻炼概念,结合了 30 分钟的激烈的心肺 ... (socket.js,行 87)
[日志] put! 回调给出了 true (socket.js,行 89)
[调试] Metronome:class-types 数据解码。 put! 完成:1.288ms (socket.js,行 93)
[日志] 向散列 19 的通道 put! (socket.js,行 86)
[日志] 消息是 [:metronome [:locations [{:studios [{:deletable false, :name "Kensington", :id 1, :locationId 1, :description "Studio (11a) sits just off Stratford Road in Stratford Studios. To find us, just pass ... (socket.js,行 87)
[调试] Metronome:locations 数据解码。 put! 完成:0.884ms (socket.js,行 93)
请注意,我们发现了一条“alts! channel hashes”的日志记录,但我们从未看到过“alts! unblocked”。然而,请注意传递给 alts! 的哈希列表。提到了通道 19,但我们随后将其放入了通道 19...但仍然没有得到解除阻塞。还有一件让我感到可疑的事情,那就是在我们被阻塞在 alts! 时,两个对 put! 的调用立即成功了,但对于仅包含一个元素的通道来说,我并不期望立即 put 回调被调用多次。有趣的是,最后的 put! 没有触发回调。
遗憾的是,重现此错误相对困难。我可以通过退出 Safari、重新打开它并导航到开发服务器的方式在某种程度上可靠地重现它。大约每 15 次尝试中就有一次会以这种方式卡住。我想知道这是否与 Safari 的 MessageChannel 实现有关 - 您可以在日志条目中看到 nexttick.js 调用其回调,这看起来就是在我的浏览器中分发的机制。
我非常愿意提供任何有用的更多信息,但现在我已无法调试它。虽然代码是专有的,但我会很高兴暂时添加人员到 Github 项目中,以尝试修复这个问题。我们有一组开发 API 服务器,您可以将它们指向目标,这样只需运行 {{lein cljs}} 就可以了。
我已经附上了我们的 Socket.io 框架和主事件循环的代码。遗憾的是,我还没有最小的测试用例 - 我真的不知道从哪里开始。