elm-animator icon indicating copy to clipboard operation
elm-animator copied to clipboard

arrived returns incorrect state

Open jerith666 opened this issue 4 years ago • 2 comments

Sometimes arrived will return an old state. This is illustrated in this ellie. Code reproduced below for reference.

Basically the program is randomly incrementing an Int every 50 - 150 ms, and updating the Timeline Int with veryQuickly which is 100 ms. When these timings overlap, the arrived value occasionally reverts to the initial state for some reason. In the example, you'll see this as the third column occasionally zeroing out.

I haven't explored all the permutations of timings, but you can vary delayMillis and fuzz in the code below to experiment.

module Sandbox.AnimatorTest exposing (..)

import Animator exposing (Animator, Timeline, arrived, current, go, toSubscription, veryQuickly, watchingWith)
import Browser exposing (Document, element)
import Delay exposing (TimeUnit(..), after)
import Dict exposing (Dict, fromList)
import Html exposing (Html, div, text)
import Html.Attributes exposing (style)
import Random exposing (Seed, float, initialSeed, step)
import String exposing (fromInt)
import Time exposing (Posix)


type alias AnimatedModel =
    { real : Int
    , timeline : Timeline Int
    , seed : Seed
    }


type AnimationMsg
    = Tick Posix
    | IncrementSomething


main : Program () AnimatedModel AnimationMsg
main =
    element
        { init = init
        , update = update
        , view = view
        , subscriptions = subscriptions
        }


delayMillis =
    100


fuzz =
    50


limit =
    8


init : () -> ( AnimatedModel, Cmd AnimationMsg )
init () =
    ( { real = 0
      , timeline = Animator.init 0
      , seed = initialSeed 1
      }
    , after delayMillis Millisecond IncrementSomething
    )


update : AnimationMsg -> AnimatedModel -> ( AnimatedModel, Cmd AnimationMsg )
update msg model =
    case msg of
        Tick posix ->
            ( Animator.update posix animator model, Cmd.none )

        IncrementSomething ->
            let
                newModel =
                    model.real + 1

                ( fuzzedDelay, newSeed ) =
                    step (float (delayMillis - fuzz) (delayMillis + fuzz)) model.seed

                maybeKeepGoing =
                    if continue newModel then
                        after fuzzedDelay Millisecond IncrementSomething

                    else
                        Cmd.none
            in
            ( { model
                | real = newModel
                , timeline = go veryQuickly newModel model.timeline
                , seed = newSeed
              }
            , maybeKeepGoing
            )


continue i =
    i <= limit


view : AnimatedModel -> Html AnimationMsg
view model =
    div
        [ style "display" "flex"
        , style "flex-direction" "row"
        ]
        [ viewImpl "real" model.real
        , viewImpl "current" <| current model.timeline
        , viewImpl "arrived" <| arrived model.timeline
        ]


viewImpl : String -> Int -> Html msg
viewImpl label i =
    div [ style "margin" "10px" ]
        [ div [] [ text label ]
        , div [] [ text <| fromInt i ]
        ]


animator : Animator AnimatedModel
animator =
    let
        updateTimeline nt m =
            { m | timeline = nt }
    in
    Animator.animator
        |> watchingWith .timeline
            updateTimeline
            continue


subscriptions : AnimatedModel -> Sub AnimationMsg
subscriptions model =
    toSubscription Tick model animator

jerith666 avatar Nov 19 '20 03:11 jerith666

I've simplified the example a bit.

jerith666 avatar Nov 20 '20 02:11 jerith666

I'm experiencing this issue as well, where my Timeline eventually reaches "Nothing", which is correctly returned by "current", but "arrived" still shows the previous "Just X".

Augustin82 avatar Nov 25 '20 20:11 Augustin82