Subscriptions

Rather than a static view of the time at which we loaded the page, we’d like our clock to update as the seconds tick by. We already know how to command Elm to fetch the current time, but we can only issue commands in our init function (when our program starts up), and in our update function (in response to a message).

What we need is a way to command Elm to send you not just a single message with the current Time, but to send you a new message once every second. This is what subscriptions are for.

Every Elm program we’ve written so far has contained a subscriptions function that returns Sub.none:

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.none

Just as Cmd.none means “I don’t have a command for Elm right now”, Sub.none means “There’s nothing I want Elm to subscribe me to right now.”

Elm’s core modules offer several functions for creating subscriptions. Time.every, for example, lets us subscribe to receive a message with the current Time every second:

subscriptions : Model -> Sub Msg
subscriptions model =
    Time.every Time.second NewTime

Run your program now, and our clock should be ticking!

Time.every uses JavaScript’s setInterval function. As you may know, setInterval isn’t guaranteed to run exactly on time, so to make sure our clock doesn’t skip any seconds, we can subscribe to more frequent events, say one every 100 milliseconds:

subscriptions : Model -> Sub Msg
subscriptions model =
    Time.every (100 * Time.millisecond) NewTime

To confirm this change is working, look at the message count on the Elm debugger at the bottom of your browser window. It should be counting up roughly ten times per second.

Notice that the subscriptions function receives your program’s current model as an argument. Elm calls subscriptions whenever your program makes a change to its model. This gives you the freedom to vary your subscriptions in response to changes in the state of your program.

For example, we could wait until our program had received the first Time value (from the command issued by our init function) before subscribing to further time updates:

subscriptions : Model -> Sub Msg
subscriptions model =
    case model.time of
        Nothing ->
            Sub.none

        Just _ ->
            Time.every (100 * Time.millisecond) NewTime

Now that we have a ticking clock, it’s time to turn it into a stopwatch!