大家好,这里有一个棘手的bug要报告......我们在ClojureScript中使用Safari 7上的core.async时遇到了一些问题。我们的应用程序围绕着一个大事件循环构建,它会阻塞在接收来自许多与用户活动或API调用相关的通道的消息之一。问题似乎在于这个事件循环中 - 我们正在使用alts!从一个可用的通道中拉取消息,但有时日志显示我们达到了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, line 62)
[日志] alts! 通道哈希值: (16 12 19 33)(main.js, line 82)
[日志] Socket 已连接。 (socket.js, line 309)
[日志] put! 到具有散列 19 的通道 (socket.js, line 86)
[日志] 消息是[:metronome [:staff [{:description null, :deletable true, :email null, :isAdmin true, :isTrainer false, :telephone null, :name "Fynder Admin", :picture null, :userId 1} {:description null, :deletable fa... (socket.js, line 87)
[日志] put! 回调给我们 true (socket.js, line 89)
[调试] Metronome: staff 数据解码。 put! 完成:12.282ms (socket.js, line 93)
[日志] put! 到具有散列 19 的通道 (socket.js, line 86)
[日志] 消息是[:metronome [:class-types [{:deletable false, :picture null, :name "CycleCore", :id 2, :description "CycleCore 是一个 55 分钟的双工作概念,结合 30 分钟的高强度心血管 ... (socket.js, line 87)
[日志] put! 回调给我们 true (socket.js, line 89)
[调试] Metronome: class-types 数据解码。 put! 完成:1.288ms (socket.js, line 93)
[日志] put! 到具有散列 19 的通道 (socket.js, line 86)
[日志] 消息是[:metronome [:locations [{:studios [{:deletable false, :name "Kensington", :id 1, :locationId 1, :description "Studio (11a) 位于 Stratford Road 的 Stratford Studios 附近。要找到我们,请穿过 ... (socket.js, line 87)
【调试】节拍器:获取位置数据解码完成。put! 完成状态:0.884ms(socket.js,行93)
请注意,我们看到了“alts!频道哈希”的日志条目,但我们从未看到“alts!解锁”。然而,请注意传给alts!的哈希列表。第19个频道被提到,但随后我们将put!应用到第19个频道上……但我们仍然没有被解锁。还有一件事让我感到可疑,那就是当我们被阻塞在alts!时,对put!的两个调用立即成功,而对一个仅需要在一次只包含一个元素的通道,我不会期望立即put回调被调用超过一次。也许我理解错了,但我不期望立即put回调被调用两次。有趣的是,最后一次put!没有调用回调。
遗憾的是,重现此错误相当困难。我可以通过退出Safari,重新打开它,然后导航到开发服务器来无故障地重现它。大约1/15的尝试将以这种方式卡住。我想知道这可能与Safari的MessageChannel实现有关 - 你可以在日志条目中看到nexttick.js如何调用其回调,这似乎是我在浏览器中使用的调度方式。
我将非常高兴提供任何有用的更多信息,但这个问题现在超出了我的调试能力。虽然代码是专有的,但我会很高兴暂时将人员添加到Github项目中,以尝试修复这个问题。我们有开发API服务器,你可以对其进行引用,所以它应该只需要运行{{lein cljs}}。
我已经附上了我们的Socket.io封装和处理主事件循环的代码。遗憾的是,我还没有一个最小测试案例 - 我真的不知道从何开始。