大家好,这里有一点棘手的错误需要报告……我们在 ClojureScript 上的 Safari 7 中使用 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.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 ... (socket.js,第 87 行)
[调试] Metronome: locations 数据解码。 put! 完成.: 0.884ms (socket.js,第 93 行)
请注意,我们看到了“alts! channel hashes”的日志条目,但从未看到“alts! unblocked”。然而,注意传递给alts!的哈希列表。提到了频道19,但随后我们将...放入频道19...但我们仍然没有被取消阻止。还有一件让我感到可疑的事情,那就是当我们处于“alts!”的阻止状态时,有两个put!调用立即成功,对于一个一次只能包含一个元素的频道。我可能理解错了什么,但我不会期望立即执行回调只被调用一次。有趣的是,最后的put!没有调用回调。
不幸的是,重现这个错误相当困难。通过退出Safari、重新打开它并导航到开发服务器,我可以相对可靠地重现它。大约有1/15的尝试会以这种方式卡住。我在想这是否与Safari的MessageChannel实现有关——你可以在日志条目中看到nexttick.js是如何调用回调的,而这看起来就像在我浏览器中是如何处理调度的。
我非常愿意帮助提供任何有用的信息,但现在我无法调试这个问题。虽然代码是保密的,但我很乐意暂时将人员添加到Github项目以尝试修复这个问题。我们有一个开发者API服务器,你可以指向这个服务器,所以只需要运行{{lein cljs}}。
我已经附上了我们的Socket.io包装器和主事件循环的代码。遗憾的是,我还没有一个最小化测试用例——我根本不知道从哪里开始。