问题描述
我正在玩弄 Elm processes 以了解有关它们如何工作的更多信息。在这部分内容中,我正在尝试实现一个计时器。
然而,我遇到了一个障碍:我无法在其余代码中找到访问流程任务结果的方法。
有那么一瞬间,我希望如果我使用 Cmd
来解决任务,Elm 运行时会很好地为我执行这种效果,但这是一个天真的想法:
type Msg
= Spawned Process.Id
| TimeIsUp
init _ =
( nothing,Task.perform Spawned (Process.spawn backgroundTask)
)
backgroundTask : Task.Task y (Platform.Cmd.Cmd Msg)
backgroundTask =
Process.sleep 1000
-- pathetic attempt to send a Msg starts here
|> Task.map ( always
<| Task.perform (always TimeIsUp)
<| Task.succeed ()
)
-- and ends here
|> Task.map (Debug.log "Timer finished") -- logs "Timer finished: <internals>"
update msg state =
case msg of
Spawned id ->
(Just id,Cmd.none)
TimeIsUp ->
(nothing,Cmd.none)
view state =
case state of
Just id ->
text "Running"
nothing ->
text "Time is up"
docs 说
没有用于进程相互通信的公共 API。
我不确定这是否意味着进程无法与应用的其余部分进行通信。
有没有办法让 update
函数在进程退出后收到 TimeIsUp
?
解决方法
有一种方法,但它需要一个地狱端口:
- 从进程中发出一个虚假的 HTTP 请求,
- 然后通过 JavaScript 拦截它
- 并将其传回给 Elm。
port ofHell : (() -> msg) -> Sub msg
subscriptions _ =
ofHell (always TimeIsUp)
backgroundTask : Task.Task y (Http.Response String)
backgroundTask =
Process.sleep 1000
-- nasty hack starts here
|> Task.andThen ( always
<| Http.task { method = "EVIL",headers = [],url = "",body = Http.emptyBody,resolver = Http.stringResolver (always Ok ""),timeout = Nothing
}
)
在幕后,Http.task
调用 new XMLHttpRequest()
,因此我们可以通过重新定义该构造函数来拦截它。
<script src="elm-app.js"></script>
<div id=hack></div>
<script>
var app = Elm.Hack.init({
node: document.getElementById('hack')
})
var orig = window.XMLHttpRequest
window.XMLHttpRequest = function () {
var req = new orig()
var orig = req.open
req.open = function (method) {
if (method == 'EVIL') {
app.ports.ofHell.send(null)
}
return orig.open.apply(this,arguments)
}
return req
}
</script>
该解决方案尚未准备好投入生产,但它确实让您可以继续使用 Elm 流程。
,Elm Processes 目前还不是一个成熟的 API。单独使用 Process
库是不可能做到的。
请参阅文档中关于 Process.spawn 的注释:
注意:这会创建一种相对受限的进程,因为它无法接收任何消息。后续版本将提供更多的用户定义流程灵活性!
以及整个 Future Plans 部分,例如:
现在,这个库非常稀少。例如,没有用于进程相互通信的公共 API。