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).
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.
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.
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:
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
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.
