问题
I'm writing a web game in Elm with lot of time-dependent events and I'm looking for a way to schedule an event at a specific time delay.
In JavaScript I used setTimeout(f, timeout), which obviously worked very well, but - for various reasons - I want to avoid JavaScript code and use Elm alone.
I'm aware that I can subscribe to Tick at specific interval and recieve clock ticks, but this is not what I want - my delays have no reasonable common denominator (for example, two of the delays are 30ms and 500ms), and I want to avoid having to handle a lot of unnecessary ticks.
I also came across Task and Process - it seems that by using them I am somehow able to what I want with Task.perform failHandler successHandler (Process.sleep Time.second).
This works, but is not very intuitive - my handlers simply ignore all possible input and send same message. Moreover, I do not expect the timeout to ever fail, so creating the failure handler feels like feeding the library, which is not what I'd expect from such an elegant language.
Is there something like Task.delayMessage time message which would do exactly what I need to (send me a copy of its message argument after specified time), or do I have to make my own wrapper for it?
回答1:
One thing that may not be obvious at first is the fact that subscriptions can change based on the model. They are effectively evaluated after every update. You can use this fact, coupled with some fields in your model, to control what subscriptions are active at any time.
Here is an example that allows for a variable cursor blink interval:
subscriptions : Model -> Sub Msg
subscriptions model =
if model.showCursor
then Time.every model.cursorBlinkInterval (always ToggleCursor)
else Sub.none
If I understand your concerns, this should overcome the potential for handling unnecessary ticks. You can have multiple subscriptions of different intervals by using Sub.batch.
回答2:
An updated and simplified version of @wintvelt's answer is now:
delay : Time.Time -> msg -> Cmd msg
delay time msg =
Process.sleep time
|> Task.perform (\_ -> msg)
with the same usage
回答3:
If you want something to happen "every x seconds", then a subscription like solution, as described by @ChadGilbert is what you need. (which is more or less like javascript's setInterval().
If, on the other hand you want something to happen only "once, after x seconds", then Process.sleep route is the way to go. This is the equivalent of javascript's setTimeOut(): after some time has passed, it does something once.
You probably have to make your own wrapper for it. Something like
-- for Elm 0.18
delay : Time -> msg -> Cmd msg
delay time msg =
Process.sleep time
|> Task.andThen (always <| Task.succeed msg)
|> Task.perform identity
To use e.g. like this:
---
update msg model =
case msg of
NewStuff somethingNew ->
...
Defer somethingNew ->
model
! [ delay (Time.second * 5) <| NewStuff somethingNew ]
来源:https://stackoverflow.com/questions/40599512/how-to-achieve-behavior-of-settimeout-in-elm