介绍
目前这比它应该做的要难一些。下一个 Elm 版本(0.15)应该通过新的语言特性和对Http
和Websocket
库的改进来解决尴尬问题。
基本问题是信号中的循环依赖性。您希望根据程序状态(“当前模型”)创建 HTTP 请求并根据 HTTP 响应更新程序状态。这应该是完全可能的,因为中间有这种异步 HTTP 处理,而不是一些不能实现的无意义的递归定义。
解决方法(hack):JavaScript 回显服务
但由于我们仍处于 Elm 0.14,我将向您展示一种解决方法。请注意,这是一个危险的黑客攻击!我将根据您给出的定义将此代码作为基础,并且仅在我重新定义的地方重复名称。评论解释了正在发生的事情。
榆木代码
-- These are the Http response strings, but coming from JavaScript through a port
port asyncResponses : Signal String
responseActions : Signal Action
responseActions = responseToAction <~ asyncResponses
-- The order of these two parameters of merge may matter. Check which should take precedence.
input : Signal Action
input = Signal.merge responseActions (Signal.subscribe updates)
-- note the redefinition:
main : Signal Html
main = Signal.foldp update initialModel input
-- These are the same Http response strings, but we're sending them out so JavaScript can listen to them.
port responses : Signal String
port responses = Http.send requests |> Signal.keepIf isSuccess (Success "") |> Signal.map (\Success s -> s)
isSuccess response = case response of
Success _ -> True
_ -> False
JS代码
Elm.fullscreen
您应该有一个 HTML 文件,您可以在其中使用或启动 Elm 程序Elm.embed
。我假设您使用全屏版本:
// Catching the returned object from Elm.fullscreen:
var program = Elm.fullscreen(Elm.Main, {asyncResponses : ""})
// JS Echo Service:
program.ports.responses.subscribe(function(s) {
program.ports.asyncResponses.send(s);
})
危险
我希望很明显,跳过这些箍是烦人和混乱的,而不是正常的 Elm 代码风格。我希望这足以阻止人们滥用它。我再说一遍,这将在即将到来的 Elm 0.15 中以更好的方式修复。
这种方法的危险在于,您向 Elm 程序发送的事件比在 JavaScript 中获得的事件多。这可能会发生在如此简单的一段 JS 上,它与它所得到的内容相呼应,这可能并不明显。但问题可能来自您的 Elm 程序。如果您的 Elm 程序为通过另一个端口获得的每个字符串从端口发送一个 Http 响应字符串,并且(例如)在其他一些输入更改您的模型时重复该响应,那么您开始累积得到回显的事件。通常 Elm 可以很聪明地处理事件同步,但是有了端口,所有的赌注都没有了,你可以通过累积事件使系统超负荷,从而使程序滞后和浏览器占用内存。所以请小心,不要把这个技巧宣传为好东西。这只是权宜之计。
资源
- 端口文件
- 使用端口的示例项目
- 关于相同问题和解决方案的简短邮件列表讨论。
- 来自 ludumdale mini 的Elm 游戏示例,它使用相同的技术来播放和停止音频。我在#elm IRC 频道上向其中一位创作者解释了这个解决方案。请注意,他们必须
dropRepeats
在传出端口上使用 a 来防止来自 JavaScript 的回显事件堆积。
- 在 Elm 0.15 中为此类事物提供了暂定的新 API 。