Chaining initialisation commands

Hi all,

I’m having difficulty wrapping my head around the differences between commands, tasks and http requests mixed together.

I have an app that is mostly functioning except for the initialisation command:

init : () -> Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
    ( Model key url [] []
    , Cmd.batch
        [ Http.send LoadManifest (Http.get manifest manifestDecoder)
        , Task.attempt Partition getViewport
        ]
    )

The http request in the batch loads data into the model from a local json file. The second task does some work on the model, which also requires Browser.Dom.getViewport information.

The first request is only every needed on init, but the second task is activated any time the window is resized via a Browser.Events.onResize subscription.

In this batch format though, there’s obviously a race condition since both commands are fired off at the same time. I need to chain them. How I should do that exactly is where I’m stuck.

Task.sequence
    [ Http.send LoadManifest (Http.get manifest manifestDecoder)
    , Task.attempt Partition getViewport
    ]

seems like the obvious first step, but now the http request is a Cmd Msg, not a Task.Task x a. I’ve attempted to coerce this using both Task.perform and Http.toTask, but I’m missing some knowledge here.

Can someone suggest a more appropriate method of initialisation here?

But what would “chain” them mean? If you mean that you want the first one to execute andThen the second one to execute, you need a Msg tag that should take both pieces of information and do something with them.

init : () -> Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
    ( Model key url [] []
    , Http.get manifest manifestDecoder
        |> Http.toTask
        |> Task.andThen
            (\manifestData ->
                Browser.Dom.getViewPort
                    |> Task.map
                        (\viewPort -> ( manifestData, viewPort ))
            )
        |> Task.attempt LoadManifestAndPartition
    )

where LoadManifestAndPartition (Result Http.Error (ManifestData, ViewPort))

If you want to LoadManifest and after loading it to trigger the Partition, you can do that from the LoadManifest handler.

I would recommend using a state machine to chain them, if you do not want them to run in parallel.

So your application set up looks something like this:

Start -> ManifestLoaded -> Partioned

with ‘Partitioned’ being the main ready state of the succesfully started application.

Try something like this:

type Model
  = Start
  | ManifestLoaded { manifest : Manifest }
  | Partioned { manifest : Manifest, layout : ViewportInfo } -- Just making this up, I don't know what you do with the viewport, but hopefully you get what I mean.

Then in init set the state to Start, and just make the Http request to load the manifest.
When that completes, in update move to the ManifestLoaded state, and request the getViewport task be run.
When that completes, in update move to the Partioned state with the viewport info.

State machines are a great way to channel the asynchronous nature of effects into sequence.

3 Likes

Of course!

Thanks guys.

Is effectively what I needed to do, although it’s good to see how you chain the andThen tasks in this situation @pdamoc.

@rupert, your answer is a good representation of what I was trying to get at.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.