13

I have a list of Cmd Msgs that need to be run in order. I'm currently using Cmd.batch list but it seems like all of them are running simultaneously such that commands that should run later are unaware of any changes to the model the earlier commands should have introduced.

I'm looking into Task.andThen but am not quite sure if that is the right direction or how to make a Cmd Msg out of a Task. Am I on the right track or is there a better way to do this, maybe that still utilizes Cmd.batch ?

I currently have two functions receive : String -> Model -> Cmd Msg and processMsg : String -> Model -> Cmd Msg:

receive : String -> Model -> Cmd Msg
receive msg model =
  msg |> String.split "&"
      |> List.map String.trim
      |> List.sort
      |> List.map (processMsg model)
      |> Cmd.batch

processMsg : String -> Model -> Cmd Msg
...  (uses Cmd.Extra.message for Msg -> Cmd Msg)

edit

edit2: I thought I could simplify the example by omitting that I use the model in receive/processMsg but I realized that I need the new model in order to form each subsequent message.

I'm trying to use Task.sequence and Task.process but to no success. I can get the first command to run successfully but I don't know how to expand this to get all commands to run:

receive : String -> Model -> Cmd Msg
receive msg model =
    let 
        msgs = 
            msg |> String.split "&" 
                |> List.map String.trim
                |> List.sort
                |> List.head
                |> Maybe.withDefault "none"
                |> Task.succeed
    in
        Task.perform Oops (processMsg model) msgs

processMsg : String -> Model -> Msg
...

I thought about changing processMsg to processMsg : String -> Task String Msg but then I have no idea what to supply for the second argument of Task.perform. I'm not sure how Task.sequence figures into this because when I try to insert it into the pipe I just end up with

List (Task x String) -> Task x (List String) -> Task x (List Msg) -> Cmd (List Msg)

which I don't know what to do with.

erp
  • 515
  • 1
  • 5
  • 14

1 Answers1

16

Task.sequence will ensure that the tasks run in sequence rather than simultaneously.

To make a Cmd msg from a task, you use Task.attempt or Task.perform.

Edit

Based on your updated question, I don't think there is a real need to use Tasks for this. It sounds like you want to chain a bunch of updates together, and that can be done by having the update function call itself recursively. You could use List.foldl to accumulate the model state and commands for each subsequent message.

Here's a quick and dirty example of what I mean:

update msg model =
  case msg of
    ChainMsgs msgs ->
      let
        chain msg1 (model1, cmds) =
          let (model2, cmds1) = update msg1 model1
          in model2 ! [ cmds, cmds1 ]
      in
        List.foldl chain (model ! []) msgs
    _ ->
      { model | message = model.message ++ ", " ++ toString msg } ! []

You are guaranteed that the messages are passed to update in the correct order, because there's no need to wrap it in tasks which makes order of execution ambiguous.

Here is a gist of this quick 'n dirty example which you can paste directly into http://elm-lang.org/try:

Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97
  • I'm having a hard time figuring out how to use `Task`s... see my above edits. Thanks – erp Sep 22 '16 at 20:48
  • @erp - I've updated my answer to show how this can be done without the need for tasks. If there is no real reason to use tasks for this, I'd recommend going with something like the above. – Chad Gilbert Sep 22 '16 at 23:11
  • I'll try to see if this works for me, but I realized that I actually need each successive new graph in order to form the next `Msg` so my actual problem may be more complex than I originally thought. – erp Sep 23 '16 at 00:05
  • If I understand your graph statement, you just need to make sure each Msg acts on the model returned by the previous Msg, right? If so, that's exactly what my foldl example is doing (see the gist for full example) – Chad Gilbert Sep 23 '16 at 00:19
  • ah yes, I meant `model`. Yeah, it works great, thanks! I just had to change `ChainMsgs (List Msg)` to `ChainMsgs (List String)` and add a `processMsg` statement into the definition of `chain` to convert the `String -> Msg`. – erp Sep 23 '16 at 01:06
  • @erp Are you on 0.18? I am trying to achieve a similar effect. Is there a place where I can take a look at your code? – lifebalance Mar 01 '17 at 10:58