@rupert,
You are likely correct, and the event queue is just where events come from after the main thread is in a waiting state. At any rate, what I said about the normal event -> update -> view -> render
cycle is not correct when the update returns a command other than Cmd.none
as the runtime then immediate performs the (possibly chained) Task
embedded in the Cmd
which causes another update
as in event -> update -> update -> ... -> view -> render
as is proven by the following code:
module Main exposing (main)
import Browser exposing (element)
import Html exposing (Html, div, button, text)
import Html.Events exposing (onClick)
import Task exposing (perform, succeed)
import Process exposing (sleep)
type alias Model = Int
type Msg = Click | Finished
myUpdate : Msg -> Model -> ( Model, Cmd Msg )
myUpdate msg mdl =
case msg of
Click ->
Debug.log ("Update Click " ++ String.fromInt mdl) <|
-- (mdl + 1, Cmd.batch [perform (\ _ -> Finished) <| succeed (), Cmd.none])
(mdl + 1, Cmd.batch [perform (\ _ -> Finished) <| sleep 1000, Cmd.none])
Finished ->
Debug.log ("Update Finished " ++ String.fromInt mdl) <|
(mdl + 1, Cmd.none)
myView : Model -> Html Msg
myView mdl =
Debug.log ("View " ++ String.fromInt mdl) <|
div []
[ button [onClick Click] [text "Click me..."]
, String.fromInt mdl |> text
]
main : Program () Model Msg
main =
element
{ init = \ _ -> ( 0, Cmd.none )
, update = myUpdate
, view = myView
, subscriptions = \ _ -> Sub.none
}
which returns the following as logged output for two clicks on the button:
View 0:
Update Click 0: (1,)
Update Finished 1: (2,)
compile:341 View 2:
Update Click 2: (3,)
Update Finished 3: (4,)
View 4:
Note that there is no update of the view between the updates. If the succeed ()
is changed to sleep 1000
by changing the non-commented active line, the output is as follows:
View 0:
Update Click 0: (1,)
View 1:
Update Finished 1: (2,)
View 2:
Update Click 2: (3,)
View 3:
Update Finished 3: (4,)
View 4:
which shows that using the sleep
to yield the main thread allows a Cmd.none
to be returned and thus a view update (and render, since the contents of the VirtualDOM change) with the rest of the dual command completing after the time delay (a second in this case, but could be much shorter allowing just enough time to complete the view update and render…
This is the way one has to allow for UI updates such as progress indications and active Cancel buttons when executing long running jobs: break them up to update the UI periodically, and restart them after a sufficient time delay for the UI to render.
So in answer to the OP, whether the usual cycle is followed is dependent on what your Cmd
is, if it performs a task that depends on the UI updating then it won’t work as the UI isn’t updated until the update outputs a Cmd.none. In the above case, Cmd.none
is output at the end of the Cmd.batch
but this only happens immediately if the commands in the batch list contain a sleep
…