# Loading some things in parallel into a record

**URL:** <https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186>\
**Category:** Request Feedback\
**Created:** [August 18, 2019, 9:57pm UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186 "2019-08-18T21:57:41Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [August 18, 2019, 9:57pm UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/1 "2019-08-18T21:57:41Z")

</div>

Hi!

I’m trying to load a few things in parallel, in my case via ports (Could be some other `Task`, not relevant to the question since the only way to do real parallelism is with `Cmd.batch` AFAIK).

- When I’m done, I’d like to store them in a record for ease of access in the application.
- When any of the things fails, I’ll move to the error state (kind of like JS `Promise.all`).

```elm
type alias Thing

type alias Things =
    { thing1 : Thing
    , thing2 : Thing
    , thing3 : Thing
    }

```

❓ **I’d love to know if there is a different or better way to do this** for this use case.

Here is how I’ve set this up a couple of times (small example):

I split my model in two steps. `Loading` with the remaining items to load, and a `Dict` for the temporary results. `Loaded` with the record for when I’m done. To load things, I request them with `Cmd.batch` passing out an identifier, which I get back with maybe a response at some point.

```elm
type Model
    = Loading Int (Dict String Thing)
    | Loaded (Maybe Things)

type Msg
    = GotThing ( String, Maybe Thing )

init : () -> ( Model, Cmd Msg )
init () =
    let
        thingsToLoad =
            [ "thing1"
            , "thing2"
            , "thing3"
            ]
    in
    ( Loading (List.length thingsToLoad) Dict.empty
    , thingsToLoad |> List.map getThing |> Cmd.batch
    )

```

When getting things out of order, I store them on the tmp `Dict`, and decrement the `remaining` int.

```elm
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case model of
        Loading remaining things ->
            case msg of
                GotThing ( name, Just thing ) ->
                    let
                        things_ =
                            Dict.insert name thing things
                    in
                    if remaining == 1 then
                        ( Loaded <| dictToThings things_, Cmd.none )

                    else
                        ( Loading (remaining - 1) things_, Cmd.none )

                GotThing ( name, Nothing ) ->
                    ( Loaded Nothing, Cmd.none )

                _ ->
                    ( model, Cmd.none )

        Loaded things ->
                ( model, Cmd.none )

```

When done, I transition to `Loaded` and parse the dict into the record:

```elm
dictToThings : Dict String Thing -> Maybe Things
dictToThings things =
    Maybe.map3
        (\t1 t2 t3 ->
            { thing1 = t1
            , thing2 = t2
            , thing3 = t3
            }
        )
        (Dict.get "thing1" things)
        (Dict.get "thing2" things)
        (Dict.get "thing3" things)

```

There are a few things I’m not particularly happy about, but it gets the job done, so I’m interested to know if there are other approaches, or maybe a library that eases this kind of parallel loading of things.

Here is an ellie with the example to tinker around: [https://ellie-app.com/6qr9whQxFBga1](https://ellie-app.com/6qr9whQxFBga1)

If a more concrete use case helps think about solutions, I have had to do this with loading assets for a game in parallel at the start, and with loading firebase data from JS in parallel when entering a new view.

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [August 18, 2019, 9:59pm UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/2 "2019-08-18T21:59:34Z")

</div>

If I had an unknown/dynamic number of things to load, then I would use a `Dict` for the loaded things too instead of a record, and I wouldn’t have the `dictToThings` function which is sort of error prone because of the order dependence of the code. Then on the application I would have to deal with `Maybe Thing`s instead. But that would be a different use case.

---

<div class="post-metadata">

**Author:** ![choonkeat](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/choonkeat/32/1555_2.png) [@choonkeat](https://discourse.elm-lang.org/u/choonkeat)\
**Post date:** [August 19, 2019, 4:59am UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/3 "2019-08-19T04:59:11Z")

</div>

Not sure if this fits your situation

If we’re talking about a known set of things, and prefer to work with a record, then I might work with a adding a “setter” function to the Msg

```auto
type Msg
    = GotThing (Model -> Thing -> Model) Thing

-- `subscriptions = always (receiveThing GotThing)` has to change..

```

then I might invoke my Cmds with the custom setters

```auto
Cmd.batch
    [ doGetThing (\m t -> { m | thing1 = t }) "thing1"
    , doGetThing (\m t -> { m | thing2 = t }) "thing2"
    ]

{-| assuming Model is now like

    type alias Model =
        { thing1 : Maybe Thing
        , thing2 : Maybe Thing
        }
-}

```

and finally update them independently without checking remaining

```auto
update msg model =
    case msg of
        GotThing setThing newThing ->
            ( setThing model newThing, Cmd.none )

```

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [August 19, 2019, 9:08am UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/4 "2019-08-19T09:08:01Z")

</div>

> [@choonkeat](#):
>
> type alias Model = { thing1 : Maybe Thing , thing2 : Maybe Thing }

The difference is that the record fields become all `Maybe`s and then any time you have to access the thing you have to deal with the maybeness.

---

<div class="post-metadata">

**Author:** ![ianmackenzie](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/ianmackenzie/32/86_2.png) [@ianmackenzie](https://discourse.elm-lang.org/u/ianmackenzie)\
**Post date:** [August 19, 2019, 4:35pm UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/5 "2019-08-19T16:35:55Z")

</div>

I think I would do something like this (untested):

```elm
type alias Things =
    { thing1 : Thing
    , thing2 : Thing
    , thing3 : Thing
    }

type alias Responses =
    { thing1 : Maybe Thing -- or perhaps 'RemoteData Thing'
    , thing2 : Maybe Thing
    , thing3 : Maybe Thing
    }

type Model
    = Loading Responses
    | Loaded Things
    | Error String

type Msg
    = GotThing1 (Result String Thing)
    | GotThing2 (Result String Thing)
    | GotThing3 (Result String Thing)

init : () -> ( Model, Cmd Msg )
init () =
    ( Loading
        { thing1 = Nothing
        , thing2 = Nothing
        , thing3 = Nothing
        }
    , Cmd.batch
        [ getThing1
        , getThing2
        , getThing3
        ]
    )

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case model of
        Error message ->
            ( model, Cmd.none )

        Loaded things ->
            ( model, Cmd.none )

        Loading responses ->
            case msg of
                GotThing1 (Err message) ->
                    ( Error message, Cmd.none )
        
                GotThing1 (Ok thing1) ->
                    checkIfLoaded { responses | thing1 = Just thing1 }

                GotThing2 (Err message) ->
                    ( Error message, Cmd.none )
        
                GotThing2 (Ok thing2) ->
                    checkIfLoaded { responses | thing2 = Just thing2 }

                GotThing3 (Err message) ->
                    ( Error message, Cmd.none )
        
                GotThing3 (Ok thing3) ->
                    checkIfLoaded { responses | thing3 = Just thing3 }

            
checkIfLoaded : Responses -> ( Model, Cmd Msg )
checkIfLoaded results =
    case Maybe.map3 Things results.thing1 results.thing2 results.thing3 of
        Just things ->
            ( Loaded things, Cmd.none )

        Nothing ->
            ( Loading results, Cmd.none )

```

This avoids the counter and the string keys, and has the advantage of extending nicely to the case where `thing1`, `thing2` and `thing3` are different types.

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [August 19, 2019, 6:16pm UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/6 "2019-08-19T18:16:11Z")

</div>

I like the explicitness and how it avoids the magic Int and String keys on the version I posted. Thanks @ianmackenzie.

---

<div class="post-metadata">

**Author:** ![andys8](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/andys8/32/280_2.png) [@andys8](https://discourse.elm-lang.org/u/andys8)\
**Post date:** [August 20, 2019, 11:33pm UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/7 "2019-08-20T23:33:02Z")

</div>

The two ideas - using a Msg containing a function and having a temporary data structure with maybes - can most likely be combined, so that `Result` handling and updating the data structure is done once instead of `n` times. Though, with this example (3 fields) it’s not really necessary.

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [August 21, 2019, 5:51am UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/8 "2019-08-21T05:51:52Z")

</div>

> [@joakin](#):
>
> the only way to do real parallelism is with `Cmd.batch`

There is no parallelism ☀ This is due to the JS virtual-machine in the browser.

Regarding the app - I would put all the individual things in a record and wrap them each in [the `RemoteData`-type](https://package.elm-lang.org/packages/krisajenkins/remotedata/latest/RemoteData). Along with that I’d write a function that looks at the record and gives a bool on whether everything is loaded successfully.

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [August 21, 2019, 6:29am UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/9 "2019-08-21T06:29:35Z")

</div>

> [@opvasger](#):
>
> There is no parallelism ☀ This is due to the JS virtual-machine in the browser.

I disagree. While JS execution is definitely single threaded, there is parallelism with workers, Image loading, HTTP requests, and now the JS realm in iframe DOM elements in some browsers.

In my case the underlying operations come from Http so they are definitely running in parallel.

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [August 21, 2019, 7:30am UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/10 "2019-08-21T07:30:10Z")

</div>

“There is no parallelism” was meant in the context of your network-requests - I wouldn’t say async network-requests are reasonable to describe as “parallelism”… But we don’t have to agree on this! ☀

Did you try the `RemoteData` thing? I think (for most cases) it’s a lot nicer than dictionaries and custom-type-models.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [August 31, 2019, 7:45am UTC](https://discourse.elm-lang.org/t/loading-some-things-in-parallel-into-a-record/4186/11 "2019-08-31T07:45:08Z")

</div>

Closed on August 31, 2019, 7:45am UTC

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