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.