如何在 Elm 中实现 setTimeout 的行为

How to achieve behavior of setTimeout in Elm

我正在用 Elm 编写一个包含大量时间相关事件的网络游戏,我正在寻找一种在特定时间延迟安排事件的方法。

在 JavaScript 中我使用了 setTimeout(f, timeout),这显然工作得很好,但是 - 由于各种原因 - 我想避免 JavaScript 代码并单独使用 Elm。

我知道我可以在特定时间间隔 subscribeTick 并接收时钟滴答声,但这不是我想要的 - 我的延迟没有合理的共同点(例如,其中两个延迟是 30 毫秒和 500 毫秒),我想避免处理很多不必要的滴答声。

我也遇到了 TaskProcess - 似乎通过使用它们我可以通过 Task.perform failHandler successHandler (Process.sleep Time.second) 以某种方式实现我想要的。

这有效,但不是很直观 - 我的处理程序只是忽略所有可能的输入并发送相同的消息。此外,我不希望超时永远失败,因此创建失败处理程序感觉就像是在为库提供数据,这不是我对这种优雅语言的期望。

是否有类似 Task.delayMessage time message 的东西可以完全满足我的需要(在指定时间后向我发送其消息参数的副本),或者我是否必须为它制作自己的包装器?

一开始可能不太明显的是,订阅可能会根据模型而变化。每次更新后都会对其进行有效评估。您可以使用这一事实,结合您模型中的某些字段,来控制哪些订阅在任何时候处于活动状态。

这是一个允许 variable cursor blink interval:

的示例
subscriptions : Model -> Sub Msg
subscriptions model =
    if model.showCursor
        then Time.every model.cursorBlinkInterval (always ToggleCursor)
        else Sub.none

如果我理解您的顾虑,这应该可以克服处理不必要的滴答声的可能性。您可以使用 Sub.batch.

进行多个不同时间间隔的订阅

如果您希望发生某些事情 "every x seconds",那么您需要一个订阅之类的解决方案,如 @ChadGilbert 所述。 (这或多或少像 javascript 的 setInterval()

另一方面,如果您只想 "once, after x seconds" 发生某些事情,那么 Process.sleep 路线就是可行的方法。这相当于 javascript 的 setTimeOut():经过一段时间后,它会执行一次操作。

您可能需要为它制作自己的包装器。像

-- for Elm 0.18
delay : Time -> msg -> Cmd msg
delay time msg =
  Process.sleep time
  |> Task.andThen (always <| Task.succeed msg)
  |> Task.perform identity

使用例如像这样:

---
update msg model =
  case msg of
    NewStuff somethingNew ->
      ...

    Defer somethingNew ->
      model
      ! [ delay (Time.second * 5) <| NewStuff somethingNew ]

Elm v0.18 的更新和简化版本是:

delay : Time.Time -> msg -> Cmd msg
delay time msg =
  Process.sleep time
  |> Task.perform (\_ -> msg)

用法相同

榆木 0.19:

执行一次并延迟:

delay : Float -> msg -> Cmd msg
delay time msg =
  Process.sleep time
  |> Task.andThen (always <| Task.succeed msg)
  |> Task.perform identity

要执行重复任务:

every : Float -> (Posix -> msg) -> Sub msg