Как достичь поведения setTimeout в Elm
Я пишу веб-игру в Elm с большим количеством событий, зависящих от времени, и я ищу способ запланировать событие с определенной задержкой времени.
В JavaScript я использовал setTimeout(f, timeout)
, который явно работал очень хорошо, но - по разным причинам - я хочу избежать кода JavaScript и использовать только Elm.
Я знаю, что я могу subscribe
до Tick
на определенном интервале и получать тики часов, но это не то, что я хочу - у моих задержек нет разумного общего знаменателя (например, две задержки - 30 мс и 500 мс), и я хочу избежать необходимости обрабатывать много ненужных тиков.
Я также наткнулся на Task
и Process
- кажется, что, используя их, я как-то умею, что хочу с помощью Task.perform failHandler successHandler (Process.sleep Time.second)
.
Это работает, но не очень интуитивно - мои обработчики просто игнорируют все возможные входные данные и отправляют одно и то же сообщение. Более того, я не ожидаю, что тайм-аут когда-нибудь потерпит неудачу, поэтому создание обработчика сбоев похоже на питание библиотеки, чего я не ожидал от такого элегантного языка.
Есть ли что-то вроде Task.delayMessage time message
, которое будет делать именно то, что мне нужно (отправьте мне копию своего аргумента сообщения после указанного времени), или мне нужно сделать для этого свою собственную оболочку?
Ответы
Ответ 1
Вначале может быть не очевидно, что подписка может измениться на основе модели. Они эффективно оцениваются после каждого обновления. Вы можете использовать этот факт в сочетании с некоторыми полями вашей модели, чтобы контролировать, какие подписки активны в любое время.
Вот пример, который позволяет интервал мигания курсора переменной:
subscriptions : Model -> Sub Msg
subscriptions model =
if model.showCursor
then Time.every model.cursorBlinkInterval (always ToggleCursor)
else Sub.none
Если я понимаю ваши проблемы, это должно преодолеть потенциал для обработки ненужных тиков. Вы можете иметь несколько подписей разных интервалов, используя Sub.batch
.
Ответ 2
Если вы хотите, чтобы что-то произошло "каждые x секунд", тогда вам понравится подписное решение, как описано @ChadGilbert. (что более или менее похоже на javascript setInterval()
.
Если, с другой стороны, вы хотите, чтобы что-то произошло только "один раз, после x секунд", тогда маршрут 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 ]
Ответ 3
Обновленная и упрощенная версия @wintvelt answer теперь:
delay : Time.Time -> msg -> Cmd msg
delay time msg =
Process.sleep time
|> Task.perform (\_ -> msg)
с тем же использованием