├── .gitignore ├── LICENSE ├── README.md ├── elm.json ├── examples ├── Api.elm ├── FetchFive.elm ├── FetchList.elm ├── FetchTwo.elm ├── README.md └── elm.json └── src └── Task └── Parallel.elm /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | *.code-workspace -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Drew Greene. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 7 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 8 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elm-task-parallel 2 | 3 | Run tasks in parallel and handle the results only if every task finishes 4 | successfully, similar to `Promise.all()` in Javascript. 5 | 6 | See the [examples folder](https://github.com/0ui/elm-task-parallel/tree/master/examples) for 7 | full code examples. 8 | 9 | ## Motivation 10 | 11 | It is common to have several tasks where the results only matter together. For 12 | example, you may need to complete multiple HTTP requests before your page can be 13 | rendered. In order to avoid running the tasks in sequence which is slower, you 14 | typically have to 15 | 16 | - Batch task commands together 17 | - Handle each task's error case 18 | - Handle each task's success case 19 | - Check if every other task is finished every time an individual task finishes 20 | 21 | This library is designed to do that for you. 22 | 23 | ## How to use 24 | 25 | Instead of using `Task.attempt`, use one of the helper functions to run up to 9 26 | tasks of different result types (or a list of the same type). It will return a 27 | tuple with some internal state and a command. 28 | 29 | ```elm 30 | import Task.Parallel as Parallel 31 | 32 | doTasks : ( Parallel.State5 Msg User Options Locations Chat Time.Posix, Cmd Msg ) 33 | doTasks = 34 | Parallel.attempt5 35 | { task1 = Api.fetchUser 36 | , task2 = Api.fetchOptions 37 | , task3 = Api.fetchLocations 38 | , task4 = Api.fetchChat 39 | , task5 = Time.now 40 | , onUpdates = TaskUpdated 41 | , onFailure = TaskFailed 42 | , onSuccess = AllFinished 43 | } 44 | ``` 45 | 46 | Store the state and pass the command to Elm. Your model will need to keep a 47 | `Parallel.State[n]` matching the number of your tasks. It will reference your `Msg` 48 | type as well as the types of your tasks. 49 | 50 | ```elm 51 | type Model 52 | = PageLoading (Parallel.State5 Msg User Options Locations Chat Time.Posix) 53 | | PageError Http.Error 54 | | PageLoaded User Options Locations Chat Time.Posix 55 | ``` 56 | The message you passed in to the helper function will need to accept an internal 57 | `Parallel.Msg[n]` referencing the types of the tasks. 58 | 59 | ```elm 60 | type Msg 61 | = TaskUpdated (Parallel.Msg5 User Options Locations Chat Time.Posix) 62 | | TaskFailed Http.Error 63 | | AllFinished User Options Locations Chat Time.Posix 64 | ``` 65 | 66 | and finally your update function will only need to handle three cases 67 | - Internal updates. Just call `Parallel.update[n]` which gives you the same 68 | type of data as the initial `Parallel.attempt[n]` 69 | - The error case where one task has failed. 70 | - The success case where all of the tasks have successfully completed. 71 | 72 | 73 | ```elm 74 | case msg of 75 | TaskUpdated taskMsg -> 76 | Parallel.update5 taskState taskMsg 77 | |> Tuple.mapFirst PageLoading 78 | 79 | TaskFailed err -> 80 | ( PageError err, Cmd.none ) 81 | 82 | AllFinished user options locations chat time -> 83 | ( PageLoaded user options locations chat time, Cmd.none ) 84 | ``` 85 | 86 | ## Caveats 87 | 88 | - If the tasks have different result types, you're limited to 9 tasks. 89 | For HTTP requests, this is a limit I haven't run into yet. For lists of tasks, 90 | there is no limit. 91 | - Updating the internal state of this library adds one case to your update 92 | function, however in the case of 9 tasks you could already have 18 cases 93 | just to update those + a completion check. This library limits those to just 94 | three. -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "0ui/elm-task-parallel", 4 | "summary": "Run tasks in parallel and handle all the results in one message.", 5 | "license": "BSD-3-Clause", 6 | "version": "2.0.0", 7 | "exposed-modules": [ 8 | "Task.Parallel" 9 | ], 10 | "elm-version": "0.19.0 <= v < 0.20.0", 11 | "dependencies": { 12 | "elm/core": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "test-dependencies": {} 15 | } 16 | -------------------------------------------------------------------------------- /examples/Api.elm: -------------------------------------------------------------------------------- 1 | module Api exposing 2 | ( fetchPhoto 3 | , fetchPost 4 | , fetchPostById 5 | , fetchComments 6 | , httpErrorString 7 | , fetchTodo 8 | , Photo 9 | , Post 10 | , Comment 11 | , Todo 12 | ) 13 | 14 | {- This dummy API makes simple GET requests with tasks to demonstrate how to 15 | use Task.Parallel. 16 | -} 17 | 18 | import Http exposing (Error(..)) 19 | import Json.Decode exposing (Decoder, bool, int, list, map4, map5, string, field) 20 | import Task exposing (Task) 21 | 22 | getTask : String -> Decoder a -> Task Error a 23 | getTask path decoder = 24 | Http.task 25 | { method = "get" 26 | , headers = [] 27 | , url = path 28 | , body = Http.emptyBody 29 | , resolver = Http.stringResolver <| handleJsonResponse decoder 30 | , timeout = Nothing 31 | } 32 | 33 | fetchPhoto : Task Error Photo 34 | fetchPhoto = 35 | getTask "https://jsonplaceholder.typicode.com/photos/1" photoDecoder 36 | 37 | fetchPost : Task Error Post 38 | fetchPost = 39 | getTask "https://jsonplaceholder.typicode.com/posts/1" postDecoder 40 | 41 | fetchPostById : Int -> Task Error Post 42 | fetchPostById id = 43 | getTask ("https://jsonplaceholder.typicode.com/posts/" ++ (String.fromInt id)) postDecoder 44 | 45 | fetchTodo : Task Error Todo 46 | fetchTodo = 47 | getTask "https://jsonplaceholder.typicode.com/todos/12" todoDecoder 48 | 49 | fetchComments : Task Error (List Comment) 50 | fetchComments = 51 | getTask "https://jsonplaceholder.typicode.com/posts/1/comments" (list commentDecoder) 52 | 53 | type alias Post = 54 | { userId : Int 55 | , id : Int 56 | , title : String 57 | , body : String 58 | } 59 | 60 | postDecoder : Decoder Post 61 | postDecoder = 62 | map4 Post 63 | (field "userId" int) 64 | (field "id" int) 65 | (field "title" string) 66 | (field "body" string) 67 | 68 | type alias Todo = 69 | { userId : Int 70 | , id : Int 71 | , title : String 72 | , completed : Bool 73 | } 74 | 75 | todoDecoder : Decoder Todo 76 | todoDecoder = 77 | map4 Todo 78 | (field "userId" int) 79 | (field "id" int) 80 | (field "title" string) 81 | (field "completed" bool) 82 | 83 | type alias Comment = 84 | { postId : Int 85 | , id : Int 86 | , name : String 87 | , email : String 88 | , body : String 89 | } 90 | 91 | commentDecoder : Decoder Comment 92 | commentDecoder = 93 | map5 Comment 94 | (field "postId" int) 95 | (field "id" int) 96 | (field "name" string) 97 | (field "email" string) 98 | (field "body" string) 99 | 100 | type alias Photo = 101 | { albumId : Int 102 | , id : Int 103 | , title : String 104 | , url : String 105 | , thumbnailUrl : String 106 | } 107 | 108 | photoDecoder : Decoder Photo 109 | photoDecoder = 110 | map5 Photo 111 | (field "albumId" int) 112 | (field "id" int) 113 | (field "title" string) 114 | (field "url" string) 115 | (field "thumbnailUrl" string) 116 | 117 | 118 | handleJsonResponse : Decoder a -> Http.Response String -> Result Error a 119 | handleJsonResponse decoder response = 120 | case response of 121 | Http.BadUrl_ url -> 122 | Err (Http.BadUrl url) 123 | 124 | Http.Timeout_ -> 125 | Err Http.Timeout 126 | 127 | Http.BadStatus_ { statusCode } _ -> 128 | Err (Http.BadStatus statusCode) 129 | 130 | Http.NetworkError_ -> 131 | Err Http.NetworkError 132 | 133 | Http.GoodStatus_ _ body -> 134 | case Json.Decode.decodeString decoder body of 135 | Err _ -> 136 | Err (Http.BadBody body) 137 | 138 | Ok result -> 139 | Ok result 140 | 141 | httpErrorString : Error -> String 142 | httpErrorString error = 143 | case error of 144 | BadUrl url -> 145 | "Bad Url: " ++ url 146 | 147 | Timeout -> 148 | "Http Timeout" 149 | 150 | NetworkError -> 151 | "Network Error" 152 | 153 | BadStatus response -> 154 | "Bad response from server: " ++ String.fromInt response 155 | 156 | BadBody message -> 157 | message -------------------------------------------------------------------------------- /examples/FetchFive.elm: -------------------------------------------------------------------------------- 1 | module FetchFive exposing (main) 2 | 3 | {- This example makes five HTTP requests before it can render the page content. 4 | It requires essentially the same number of lines as the example with only two 5 | requests. 6 | -} 7 | 8 | import Api exposing (Comment, Photo, Post, Todo) 9 | import Browser 10 | import Html exposing (Html, div, h1, h2, img, li, p, text, ul) 11 | import Html.Attributes exposing (src) 12 | import Http 13 | import Task.Parallel as Parallel 14 | import Time 15 | 16 | 17 | main = 18 | Browser.element 19 | { init = init 20 | , update = update 21 | , subscriptions = always Sub.none 22 | , view = view 23 | } 24 | 25 | 26 | type Model 27 | = Loading (Parallel.State5 Msg Post (List Comment) Time.Posix Photo Todo) 28 | | FailedToLoad String 29 | | PageReady Post (List Comment) Time.Posix Photo Todo 30 | 31 | 32 | init : () -> ( Model, Cmd Msg ) 33 | init _ = 34 | let 35 | ( loadingState, fetchCmd ) = 36 | Parallel.attempt5 37 | { task1 = Api.fetchPost 38 | , task2 = Api.fetchComments 39 | , task3 = Time.now 40 | , task4 = Api.fetchPhoto 41 | , task5 = Api.fetchTodo 42 | , onUpdates = TaskUpdated 43 | , onFailure = DownloadFailed 44 | , onSuccess = AllFinished 45 | } 46 | in 47 | ( Loading loadingState, fetchCmd ) 48 | 49 | 50 | type Msg 51 | = TaskUpdated (Parallel.Msg5 Post (List Comment) Time.Posix Photo Todo) 52 | | DownloadFailed Http.Error 53 | | AllFinished Post (List Comment) Time.Posix Photo Todo 54 | 55 | 56 | update : Msg -> Model -> ( Model, Cmd Msg ) 57 | update msg model = 58 | case model of 59 | Loading downloadState -> 60 | case msg of 61 | TaskUpdated downloadMsg -> 62 | Parallel.update5 downloadState downloadMsg 63 | |> Tuple.mapFirst Loading 64 | 65 | DownloadFailed err -> 66 | ( FailedToLoad <| Api.httpErrorString <| err, Cmd.none ) 67 | 68 | AllFinished post comments time photo todo -> 69 | ( PageReady post comments time photo todo, Cmd.none ) 70 | 71 | _ -> 72 | ( model, Cmd.none ) 73 | 74 | 75 | view : Model -> Html Msg 76 | view model = 77 | case model of 78 | Loading _ -> 79 | text "Loading data..." 80 | 81 | FailedToLoad err -> 82 | text <| "Failed to load: " ++ err 83 | 84 | PageReady post comments time photo todo -> 85 | div [] 86 | [ h1 [] [ text post.title ] 87 | , h2 [] [ text <| "in the year " ++ (time |> Time.toYear Time.utc |> String.fromInt) ] 88 | , p [] [ text post.body ] 89 | , p [] 90 | [ text <| 91 | "Todo is " 92 | ++ (if todo.completed then 93 | "done" 94 | 95 | else 96 | "not done" 97 | ) 98 | ] 99 | , img [ src photo.thumbnailUrl ] [] 100 | , ul [] 101 | (comments 102 | |> List.map 103 | (\comment -> 104 | li [] 105 | [ p [] [ text comment.name ] 106 | , p [] [ text comment.body ] 107 | ] 108 | ) 109 | ) 110 | ] 111 | -------------------------------------------------------------------------------- /examples/FetchList.elm: -------------------------------------------------------------------------------- 1 | module FetchList exposing (main) 2 | 3 | {- This example fetches a list of HTTP tasks. 4 | -} 5 | 6 | import Api exposing (Post, Comment) 7 | import Browser 8 | import Html exposing (Html, div, text, h1, p, ul, li) 9 | import Http 10 | import Task.Parallel as Parallel 11 | 12 | main = 13 | Browser.element 14 | { init = init 15 | , update = update 16 | , subscriptions = always Sub.none 17 | , view = view 18 | } 19 | 20 | type Model 21 | = Loading (Parallel.ListState Msg Post) 22 | | FailedToLoad String 23 | | PageReady (List Post) 24 | 25 | 26 | init : () -> (Model, Cmd Msg) 27 | init _ = 28 | let 29 | ( loadingState, fetchCmd ) = 30 | Parallel.attemptList 31 | { tasks = 32 | [ Api.fetchPostById 1 33 | , Api.fetchPostById 2 34 | , Api.fetchPostById 42 35 | , Api.fetchPostById 4 36 | , Api.fetchPostById 5 37 | , Api.fetchPostById 12 38 | ] 39 | , onUpdates = DownloadUpdated 40 | , onFailure = DownloadFailed 41 | , onSuccess = DownloadFinished 42 | } 43 | in 44 | ( Loading loadingState, fetchCmd ) 45 | 46 | 47 | type Msg 48 | = DownloadUpdated (Parallel.ListMsg Post) 49 | | DownloadFailed Http.Error 50 | | DownloadFinished (List Post) 51 | 52 | 53 | update : Msg -> Model -> (Model, Cmd Msg) 54 | update msg model = 55 | case model of 56 | Loading downloadState -> 57 | case msg of 58 | DownloadUpdated downloadMsg -> 59 | Parallel.updateList downloadState downloadMsg 60 | |> Tuple.mapFirst Loading 61 | 62 | DownloadFailed err -> 63 | ( FailedToLoad <| Api.httpErrorString <| err, Cmd.none ) 64 | 65 | DownloadFinished posts -> 66 | ( PageReady posts, Cmd.none ) 67 | _ -> 68 | ( model, Cmd.none ) 69 | 70 | view : Model -> Html Msg 71 | view model = 72 | case model of 73 | Loading _ -> 74 | text "Loading data..." 75 | 76 | FailedToLoad err -> 77 | text <| "Failed to load: " ++ err 78 | 79 | PageReady posts -> 80 | div [] 81 | [ h1 [] [ text "Posts" ] 82 | , ul [] 83 | (posts 84 | |> List.map(\post -> 85 | li [] 86 | [ p [] [ text post.title ] 87 | , p [] [ text post.body ] 88 | , p [] [ text <| String.fromInt <| post.id ] 89 | ] 90 | ) 91 | ) 92 | ] 93 | 94 | -------------------------------------------------------------------------------- /examples/FetchTwo.elm: -------------------------------------------------------------------------------- 1 | module FetchTwo exposing (main) 2 | 3 | {- This example makes two HTTP requests before it can render the page content. 4 | -} 5 | 6 | import Api exposing (Comment, Post) 7 | import Browser 8 | import Html exposing (Html, div, h1, li, p, text, ul) 9 | import Http 10 | import Task.Parallel as Parallel 11 | 12 | 13 | main = 14 | Browser.element 15 | { init = init 16 | , update = update 17 | , subscriptions = always Sub.none 18 | , view = view 19 | } 20 | 21 | 22 | type Model 23 | = Loading (Parallel.State2 Msg Post (List Comment)) 24 | | FailedToLoad String 25 | | PageReady Post (List Comment) 26 | 27 | 28 | init : () -> ( Model, Cmd Msg ) 29 | init _ = 30 | let 31 | ( loadingState, fetchCmd ) = 32 | Parallel.attempt2 33 | { task1 = Api.fetchPost 34 | , task2 = Api.fetchComments 35 | , onUpdates = DownloadUpdated 36 | , onFailure = DownloadFailed 37 | , onSuccess = DownloadFinished 38 | } 39 | in 40 | ( Loading loadingState, fetchCmd ) 41 | 42 | 43 | type Msg 44 | = DownloadUpdated (Parallel.Msg2 Post (List Comment)) 45 | | DownloadFailed Http.Error 46 | | DownloadFinished Post (List Comment) 47 | 48 | 49 | update : Msg -> Model -> ( Model, Cmd Msg ) 50 | update msg model = 51 | case model of 52 | Loading downloadState -> 53 | case msg of 54 | DownloadUpdated downloadMsg -> 55 | Parallel.update2 downloadState downloadMsg 56 | |> Tuple.mapFirst Loading 57 | 58 | DownloadFailed err -> 59 | ( FailedToLoad <| Api.httpErrorString <| err, Cmd.none ) 60 | 61 | DownloadFinished post comments -> 62 | ( PageReady post comments, Cmd.none ) 63 | 64 | _ -> 65 | ( model, Cmd.none ) 66 | 67 | 68 | view : Model -> Html Msg 69 | view model = 70 | case model of 71 | Loading _ -> 72 | text "Loading data..." 73 | 74 | FailedToLoad err -> 75 | text <| "Failed to load: " ++ err 76 | 77 | PageReady post comments -> 78 | div [] 79 | [ h1 [] [ text post.title ] 80 | , p [] [ text post.body ] 81 | , ul [] 82 | (comments 83 | |> List.map 84 | (\comment -> 85 | li [] 86 | [ p [] [ text comment.name ] 87 | , p [] [ text comment.body ] 88 | ] 89 | ) 90 | ) 91 | ] 92 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## [Two Tasks](FetchTwo.elm) 2 | 3 | Run two tasks in parallel and get the results. 4 | 5 | ## [Five Tasks](FetchFive.elm) 6 | 7 | This example increases the number of tasks to five but uses the same principles. 8 | 9 | ## [List of tasks](FetchList.elm) 10 | 11 | Run a list of tasks of arbitrary size and get the results. -------------------------------------------------------------------------------- /examples/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | ".", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.2", 11 | "elm/core": "1.0.5", 12 | "elm/html": "1.0.0", 13 | "elm/http": "2.0.0", 14 | "elm/json": "1.1.3", 15 | "elm/time": "1.0.0" 16 | }, 17 | "indirect": { 18 | "elm/bytes": "1.0.8", 19 | "elm/file": "1.0.5", 20 | "elm/url": "1.0.0", 21 | "elm/virtual-dom": "1.0.2" 22 | } 23 | }, 24 | "test-dependencies": { 25 | "direct": {}, 26 | "indirect": {} 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Task/Parallel.elm: -------------------------------------------------------------------------------- 1 | module Task.Parallel exposing 2 | ( attempt2, attempt3, attempt4, attempt5, attempt6, attempt7, attempt8, attempt9, attemptList 3 | , attempt 4 | , update2, update3, update4, update5, update6, update7, update8, update9, updateList 5 | , State2, State3, State4, State5, State6, State7, State8, State9, Msg2, Msg3, Msg4, Msg5, Msg6, Msg7, Msg8, Msg9, ListState, ListMsg 6 | ) 7 | 8 | {-| This library helps you run tasks in parallel when you only need the results 9 | if every task finishes successfully, similar to `Promise.all()` in Javascript. A 10 | good use case is handling the result of multiple HTTP requests. 11 | 12 | 13 | ## Task Helpers 14 | 15 | @docs attempt2, attempt3, attempt4, attempt5, attempt6, attempt7, attempt8, attempt9, attemptList 16 | 17 | 18 | ## Less Common Helpers 19 | 20 | @docs attempt 21 | 22 | 23 | ## Update 24 | 25 | You will have to pass internal messages and commands along in your update 26 | function in order to eventually get your results. 27 | 28 | @docs update2, update3, update4, update5, update6, update7, update8, update9, updateList 29 | 30 | 31 | ## Types 32 | 33 | @docs State2, State3, State4, State5, State6, State7, State8, State9, Msg2, Msg3, Msg4, Msg5, Msg6, Msg7, Msg8, Msg9, ListState, ListMsg 34 | 35 | -} 36 | 37 | import Task exposing (Task) 38 | 39 | 40 | {-| Opaque type for storing state of tasks. 41 | -} 42 | type State2 msg a b 43 | = State2 (a -> b -> msg) (Maybe a) (Maybe b) 44 | 45 | 46 | {-| -} 47 | type State3 msg a b c 48 | = State3 (a -> b -> c -> msg) (Maybe a) (Maybe b) (Maybe c) 49 | 50 | 51 | {-| -} 52 | type State4 msg a b c d 53 | = State4 (a -> b -> c -> d -> msg) (Maybe a) (Maybe b) (Maybe c) (Maybe d) 54 | 55 | 56 | {-| -} 57 | type State5 msg a b c d e 58 | = State5 (a -> b -> c -> d -> e -> msg) (Maybe a) (Maybe b) (Maybe c) (Maybe d) (Maybe e) 59 | 60 | 61 | {-| -} 62 | type State6 msg a b c d e f 63 | = State6 (a -> b -> c -> d -> e -> f -> msg) (Maybe a) (Maybe b) (Maybe c) (Maybe d) (Maybe e) (Maybe f) 64 | 65 | 66 | {-| -} 67 | type State7 msg a b c d e f g 68 | = State7 (a -> b -> c -> d -> e -> f -> g -> msg) (Maybe a) (Maybe b) (Maybe c) (Maybe d) (Maybe e) (Maybe f) (Maybe g) 69 | 70 | 71 | {-| -} 72 | type State8 msg a b c d e f g h 73 | = State8 (a -> b -> c -> d -> e -> f -> g -> h -> msg) (Maybe a) (Maybe b) (Maybe c) (Maybe d) (Maybe e) (Maybe f) (Maybe g) (Maybe h) 74 | 75 | 76 | {-| -} 77 | type State9 msg a b c d e f g h i 78 | = State9 (a -> b -> c -> d -> e -> f -> g -> h -> i -> msg) (Maybe a) (Maybe b) (Maybe c) (Maybe d) (Maybe e) (Maybe f) (Maybe g) (Maybe h) (Maybe i) 79 | 80 | 81 | {-| Opaque type for updating state of tasks. 82 | -} 83 | type Msg2 a b 84 | = LoadedA2 a 85 | | LoadedB2 b 86 | 87 | 88 | {-| -} 89 | type Msg3 a b c 90 | = LoadedA3 a 91 | | LoadedB3 b 92 | | LoadedC3 c 93 | 94 | 95 | {-| -} 96 | type Msg4 a b c d 97 | = LoadedA4 a 98 | | LoadedB4 b 99 | | LoadedC4 c 100 | | LoadedD4 d 101 | 102 | 103 | {-| -} 104 | type Msg5 a b c d e 105 | = LoadedA5 a 106 | | LoadedB5 b 107 | | LoadedC5 c 108 | | LoadedD5 d 109 | | LoadedE5 e 110 | 111 | 112 | {-| -} 113 | type Msg6 a b c d e f 114 | = LoadedA6 a 115 | | LoadedB6 b 116 | | LoadedC6 c 117 | | LoadedD6 d 118 | | LoadedE6 e 119 | | LoadedF6 f 120 | 121 | 122 | {-| -} 123 | type Msg7 a b c d e f g 124 | = LoadedA7 a 125 | | LoadedB7 b 126 | | LoadedC7 c 127 | | LoadedD7 d 128 | | LoadedE7 e 129 | | LoadedF7 f 130 | | LoadedG7 g 131 | 132 | 133 | {-| -} 134 | type Msg8 a b c d e f g h 135 | = LoadedA8 a 136 | | LoadedB8 b 137 | | LoadedC8 c 138 | | LoadedD8 d 139 | | LoadedE8 e 140 | | LoadedF8 f 141 | | LoadedG8 g 142 | | LoadedH8 h 143 | 144 | 145 | {-| -} 146 | type Msg9 a b c d e f g h i 147 | = LoadedA9 a 148 | | LoadedB9 b 149 | | LoadedC9 c 150 | | LoadedD9 d 151 | | LoadedE9 e 152 | | LoadedF9 f 153 | | LoadedG9 g 154 | | LoadedH9 h 155 | | LoadedI9 i 156 | 157 | 158 | {-| Attempt a single task. The benefit of this over Task.attempt is that it 159 | handles routing the result to the provided success and failure messages. You can 160 | reuse those error messages for different tasks if they're handled the same way. 161 | Since there are no other tasks, you don't need to manage any additional updates. 162 | 163 | type Msg 164 | = ErrorOcurred Http.Error 165 | | FetchCompleted MyData 166 | 167 | doTask : Cmd Msg 168 | doTask = 169 | attempt FetchCompleted ErrorOcurred fetchMyData 170 | 171 | -} 172 | attempt : (a -> msg) -> (x -> msg) -> Task x a -> Cmd msg 173 | attempt successMsg failureMsg task1 = 174 | task1 |> routeTo successMsg failureMsg 175 | 176 | 177 | {-| Attempt two tasks which will send an update when either all tasks finish 178 | successfully or one fails. The returned `State` will be used in your main 179 | update function to call [`update`](#update) and pass internal messages. 180 | 181 | type Msg 182 | = TaskStateUpdated (Task.Parallel.Msg2 String Int) 183 | | OneTaskFailed Http.Error 184 | | AllTasksCompleted String Int 185 | 186 | doTask : ( Task.Parallel.State2 Msg String Int, Cmd Msg ) 187 | doTask = 188 | attempt2 189 | { task1 = fetchString 190 | , task2 = fetchInt 191 | , onUpdates = TaskStateUpdated 192 | , onFailure = OneTaskFailed 193 | , onSuccess = AllTasksCompleted 194 | } 195 | 196 | -} 197 | attempt2 : 198 | { task1 : Task x a 199 | , task2 : Task x b 200 | , onUpdates : Msg2 a b -> msg 201 | , onSuccess : a -> b -> msg 202 | , onFailure : x -> msg 203 | } 204 | -> ( State2 msg a b, Cmd msg ) 205 | attempt2 { task1, task2, onUpdates, onSuccess, onFailure } = 206 | ( State2 onSuccess Nothing Nothing 207 | , [ task1 |> routeTo (onUpdates << LoadedA2) onFailure 208 | , task2 |> routeTo (onUpdates << LoadedB2) onFailure 209 | ] 210 | |> Cmd.batch 211 | ) 212 | 213 | 214 | {-| -} 215 | attempt3 : 216 | { task1 : Task x a 217 | , task2 : Task x b 218 | , task3 : Task x c 219 | , onUpdates : Msg3 a b c -> msg 220 | , onSuccess : a -> b -> c -> msg 221 | , onFailure : x -> msg 222 | } 223 | -> ( State3 msg a b c, Cmd msg ) 224 | attempt3 { task1, task2, task3, onUpdates, onSuccess, onFailure } = 225 | ( State3 onSuccess Nothing Nothing Nothing 226 | , [ task1 |> routeTo (onUpdates << LoadedA3) onFailure 227 | , task2 |> routeTo (onUpdates << LoadedB3) onFailure 228 | , task3 |> routeTo (onUpdates << LoadedC3) onFailure 229 | ] 230 | |> Cmd.batch 231 | ) 232 | 233 | 234 | {-| -} 235 | attempt4 : 236 | { task1 : Task x a 237 | , task2 : Task x b 238 | , task3 : Task x c 239 | , task4 : Task x d 240 | , onUpdates : Msg4 a b c d -> msg 241 | , onSuccess : a -> b -> c -> d -> msg 242 | , onFailure : x -> msg 243 | } 244 | -> ( State4 msg a b c d, Cmd msg ) 245 | attempt4 { task1, task2, task3, task4, onUpdates, onSuccess, onFailure } = 246 | ( State4 onSuccess Nothing Nothing Nothing Nothing 247 | , [ task1 |> routeTo (onUpdates << LoadedA4) onFailure 248 | , task2 |> routeTo (onUpdates << LoadedB4) onFailure 249 | , task3 |> routeTo (onUpdates << LoadedC4) onFailure 250 | , task4 |> routeTo (onUpdates << LoadedD4) onFailure 251 | ] 252 | |> Cmd.batch 253 | ) 254 | 255 | 256 | {-| -} 257 | attempt5 : 258 | { task1 : Task x a 259 | , task2 : Task x b 260 | , task3 : Task x c 261 | , task4 : Task x d 262 | , task5 : Task x e 263 | , onUpdates : Msg5 a b c d e -> msg 264 | , onSuccess : a -> b -> c -> d -> e -> msg 265 | , onFailure : x -> msg 266 | } 267 | -> ( State5 msg a b c d e, Cmd msg ) 268 | attempt5 { task1, task2, task3, task4, task5, onUpdates, onSuccess, onFailure } = 269 | ( State5 onSuccess Nothing Nothing Nothing Nothing Nothing 270 | , [ task1 |> routeTo (onUpdates << LoadedA5) onFailure 271 | , task2 |> routeTo (onUpdates << LoadedB5) onFailure 272 | , task3 |> routeTo (onUpdates << LoadedC5) onFailure 273 | , task4 |> routeTo (onUpdates << LoadedD5) onFailure 274 | , task5 |> routeTo (onUpdates << LoadedE5) onFailure 275 | ] 276 | |> Cmd.batch 277 | ) 278 | 279 | {-| -} 280 | attempt6 : 281 | { task1 : Task x a 282 | , task2 : Task x b 283 | , task3 : Task x c 284 | , task4 : Task x d 285 | , task5 : Task x e 286 | , task6 : Task x f 287 | , onUpdates : Msg6 a b c d e f -> msg 288 | , onSuccess : a -> b -> c -> d -> e -> f -> msg 289 | , onFailure : x -> msg 290 | } 291 | -> ( State6 msg a b c d e f, Cmd msg ) 292 | attempt6 { task1, task2, task3, task4, task5, task6, onUpdates, onSuccess, onFailure } = 293 | ( State6 onSuccess Nothing Nothing Nothing Nothing Nothing Nothing 294 | , [ task1 |> routeTo (onUpdates << LoadedA6) onFailure 295 | , task2 |> routeTo (onUpdates << LoadedB6) onFailure 296 | , task3 |> routeTo (onUpdates << LoadedC6) onFailure 297 | , task4 |> routeTo (onUpdates << LoadedD6) onFailure 298 | , task5 |> routeTo (onUpdates << LoadedE6) onFailure 299 | , task6 |> routeTo (onUpdates << LoadedF6) onFailure 300 | ] 301 | |> Cmd.batch 302 | ) 303 | 304 | 305 | {-| -} 306 | attempt7 : 307 | { task1 : Task x a 308 | , task2 : Task x b 309 | , task3 : Task x c 310 | , task4 : Task x d 311 | , task5 : Task x e 312 | , task6 : Task x f 313 | , task7 : Task x g 314 | , onUpdates : Msg7 a b c d e f g -> msg 315 | , onSuccess : a -> b -> c -> d -> e -> f -> g -> msg 316 | , onFailure : x -> msg 317 | } 318 | -> ( State7 msg a b c d e f g, Cmd msg ) 319 | attempt7 { task1, task2, task3, task4, task5, task6, task7, onUpdates, onSuccess, onFailure } = 320 | ( State7 onSuccess Nothing Nothing Nothing Nothing Nothing Nothing Nothing 321 | , [ task1 |> routeTo (onUpdates << LoadedA7) onFailure 322 | , task2 |> routeTo (onUpdates << LoadedB7) onFailure 323 | , task3 |> routeTo (onUpdates << LoadedC7) onFailure 324 | , task4 |> routeTo (onUpdates << LoadedD7) onFailure 325 | , task5 |> routeTo (onUpdates << LoadedE7) onFailure 326 | , task6 |> routeTo (onUpdates << LoadedF7) onFailure 327 | , task7 |> routeTo (onUpdates << LoadedG7) onFailure 328 | ] 329 | |> Cmd.batch 330 | ) 331 | 332 | 333 | {-| -} 334 | attempt8 : 335 | { task1 : Task x a 336 | , task2 : Task x b 337 | , task3 : Task x c 338 | , task4 : Task x d 339 | , task5 : Task x e 340 | , task6 : Task x f 341 | , task7 : Task x g 342 | , task8 : Task x h 343 | , onUpdates : Msg8 a b c d e f g h -> msg 344 | , onSuccess : a -> b -> c -> d -> e -> f -> g -> h -> msg 345 | , onFailure : x -> msg 346 | } 347 | -> ( State8 msg a b c d e f g h, Cmd msg ) 348 | attempt8 { task1, task2, task3, task4, task5, task6, task7, task8, onUpdates, onSuccess, onFailure } = 349 | ( State8 onSuccess Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing 350 | , [ task1 |> routeTo (onUpdates << LoadedA8) onFailure 351 | , task2 |> routeTo (onUpdates << LoadedB8) onFailure 352 | , task3 |> routeTo (onUpdates << LoadedC8) onFailure 353 | , task4 |> routeTo (onUpdates << LoadedD8) onFailure 354 | , task5 |> routeTo (onUpdates << LoadedE8) onFailure 355 | , task6 |> routeTo (onUpdates << LoadedF8) onFailure 356 | , task7 |> routeTo (onUpdates << LoadedG8) onFailure 357 | , task8 |> routeTo (onUpdates << LoadedH8) onFailure 358 | ] 359 | |> Cmd.batch 360 | ) 361 | 362 | 363 | {-| -} 364 | attempt9 : 365 | { task1 : Task x a 366 | , task2 : Task x b 367 | , task3 : Task x c 368 | , task4 : Task x d 369 | , task5 : Task x e 370 | , task6 : Task x f 371 | , task7 : Task x g 372 | , task8 : Task x h 373 | , task9 : Task x i 374 | , onUpdates : Msg9 a b c d e f g h i -> msg 375 | , onSuccess : a -> b -> c -> d -> e -> f -> g -> h -> i -> msg 376 | , onFailure : x -> msg 377 | } 378 | -> ( State9 msg a b c d e f g h i, Cmd msg ) 379 | attempt9 { task1, task2, task3, task4, task5, task6, task7, task8, task9, onUpdates, onSuccess, onFailure } = 380 | ( State9 onSuccess Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing 381 | , [ task1 |> routeTo (onUpdates << LoadedA9) onFailure 382 | , task2 |> routeTo (onUpdates << LoadedB9) onFailure 383 | , task3 |> routeTo (onUpdates << LoadedC9) onFailure 384 | , task4 |> routeTo (onUpdates << LoadedD9) onFailure 385 | , task5 |> routeTo (onUpdates << LoadedE9) onFailure 386 | , task6 |> routeTo (onUpdates << LoadedF9) onFailure 387 | , task7 |> routeTo (onUpdates << LoadedG9) onFailure 388 | , task8 |> routeTo (onUpdates << LoadedH9) onFailure 389 | , task9 |> routeTo (onUpdates << LoadedI9) onFailure 390 | ] 391 | |> Cmd.batch 392 | ) 393 | 394 | 395 | {-| Handle updates for two tasks by calling `update2` inside of your main update 396 | function to keep this library's internal state updated. If they have either all 397 | finished successfully or one has failed, the corresponding message you provided 398 | will be sent to your main `update` function. Maintain a copy of the returned 399 | state to pass in on each subsequent `update`. This step is required with 400 | `attempt[n]` functions. 401 | 402 | type Msg 403 | = DownloadUpdated (Task.Parallel.Msg2 Actor Film) 404 | | DownloadFailed Http.Error 405 | | DownloadCompleted Actor Film 406 | 407 | update : Msg -> Model -> ( Model, Cmd Msg ) 408 | update msg model = 409 | case msg of 410 | DownloadUpdated taskMsg -> 411 | let 412 | ( nextTaskState, nextCmd ) = 413 | Task.Parallel.update2 model.taskState taskMsg 414 | in 415 | ( { model | taskState = nextTaskState }, nextCmd ) 416 | 417 | DownloadCompleted actor film -> 418 | ( { model | actor = actor, film = film, Cmd.none ) 419 | 420 | DownloadFailed err -> 421 | ( { model | loadingError = Just err }, Cmd.none ) 422 | 423 | -} 424 | update2 : State2 msg a b -> Msg2 a b -> ( State2 msg a b, Cmd msg ) 425 | update2 (State2 onSuccess a b) msg = 426 | let 427 | next a_ b_ = 428 | ( State2 onSuccess a_ b_, Maybe.map2 onSuccess a_ b_ |> toCmd ) 429 | in 430 | case msg of 431 | LoadedA2 data -> 432 | next (Just data) b 433 | 434 | LoadedB2 data -> 435 | next a (Just data) 436 | 437 | 438 | {-| -} 439 | update3 : State3 msg a b c -> Msg3 a b c -> ( State3 msg a b c, Cmd msg ) 440 | update3 (State3 onSuccess a b c) msg = 441 | let 442 | next a_ b_ c_ = 443 | ( State3 onSuccess a_ b_ c_, Maybe.map3 onSuccess a_ b_ c_ |> toCmd ) 444 | in 445 | case msg of 446 | LoadedA3 data -> 447 | next (Just data) b c 448 | 449 | LoadedB3 data -> 450 | next a (Just data) c 451 | 452 | LoadedC3 data -> 453 | next a b (Just data) 454 | 455 | 456 | {-| -} 457 | update4 : State4 msg a b c d -> Msg4 a b c d -> ( State4 msg a b c d, Cmd msg ) 458 | update4 (State4 onSuccess a b c d) msg = 459 | let 460 | next a_ b_ c_ d_ = 461 | ( State4 onSuccess a_ b_ c_ d_, Maybe.map4 onSuccess a_ b_ c_ d_ |> toCmd ) 462 | in 463 | case msg of 464 | LoadedA4 data -> 465 | next (Just data) b c d 466 | 467 | LoadedB4 data -> 468 | next a (Just data) c d 469 | 470 | LoadedC4 data -> 471 | next a b (Just data) d 472 | 473 | LoadedD4 data -> 474 | next a b c (Just data) 475 | 476 | 477 | {-| -} 478 | update5 : State5 msg a b c d e -> Msg5 a b c d e -> ( State5 msg a b c d e, Cmd msg ) 479 | update5 (State5 onSuccess a b c d e) msg = 480 | let 481 | next a_ b_ c_ d_ e_ = 482 | ( State5 onSuccess a_ b_ c_ d_ e_, Maybe.map5 onSuccess a_ b_ c_ d_ e_ |> toCmd ) 483 | in 484 | case msg of 485 | LoadedA5 data -> 486 | next (Just data) b c d e 487 | 488 | LoadedB5 data -> 489 | next a (Just data) c d e 490 | 491 | LoadedC5 data -> 492 | next a b (Just data) d e 493 | 494 | LoadedD5 data -> 495 | next a b c (Just data) e 496 | 497 | LoadedE5 data -> 498 | next a b c d (Just data) 499 | 500 | 501 | {-| -} 502 | update6 : State6 msg a b c d e f -> Msg6 a b c d e f -> ( State6 msg a b c d e f, Cmd msg ) 503 | update6 (State6 onSuccess a b c d e f) msg = 504 | let 505 | next a_ b_ c_ d_ e_ f_ = 506 | ( State6 onSuccess a_ b_ c_ d_ e_ f_ 507 | , Maybe.map5 508 | (\a6 b6 c6 d6 e6 -> 509 | Maybe.map (\f6 -> onSuccess a6 b6 c6 d6 e6 f6) f_ 510 | ) 511 | a_ b_ c_ d_ e_ 512 | |> Maybe.withDefault Nothing 513 | |> toCmd 514 | ) 515 | in 516 | case msg of 517 | LoadedA6 data -> 518 | next (Just data) b c d e f 519 | 520 | LoadedB6 data -> 521 | next a (Just data) c d e f 522 | 523 | LoadedC6 data -> 524 | next a b (Just data) d e f 525 | 526 | LoadedD6 data -> 527 | next a b c (Just data) e f 528 | 529 | LoadedE6 data -> 530 | next a b c d (Just data) f 531 | 532 | LoadedF6 data -> 533 | next a b c d e (Just data) 534 | 535 | {-| -} 536 | update7 : State7 msg a b c d e f g -> Msg7 a b c d e f g -> ( State7 msg a b c d e f g, Cmd msg ) 537 | update7 (State7 onSuccess a b c d e f g) msg = 538 | let 539 | next a_ b_ c_ d_ e_ f_ g_ = 540 | ( State7 onSuccess a_ b_ c_ d_ e_ f_ g_ 541 | , Maybe.map5 542 | (\a7 b7 c7 d7 e7 -> 543 | Maybe.map2 (\f7 g7 -> onSuccess a7 b7 c7 d7 e7 f7 g7) f_ g_ 544 | ) 545 | a_ b_ c_ d_ e_ 546 | |> Maybe.withDefault Nothing 547 | |> toCmd 548 | ) 549 | in 550 | case msg of 551 | LoadedA7 data -> 552 | next (Just data) b c d e f g 553 | 554 | LoadedB7 data -> 555 | next a (Just data) c d e f g 556 | 557 | LoadedC7 data -> 558 | next a b (Just data) d e f g 559 | 560 | LoadedD7 data -> 561 | next a b c (Just data) e f g 562 | 563 | LoadedE7 data -> 564 | next a b c d (Just data) f g 565 | 566 | LoadedF7 data -> 567 | next a b c d e (Just data) g 568 | 569 | LoadedG7 data -> 570 | next a b c d e f (Just data) 571 | 572 | 573 | {-| -} 574 | update8 : State8 msg a b c d e f g h -> Msg8 a b c d e f g h -> ( State8 msg a b c d e f g h, Cmd msg ) 575 | update8 (State8 onSuccess a b c d e f g h) msg = 576 | let 577 | next a_ b_ c_ d_ e_ f_ g_ h_ = 578 | ( State8 onSuccess a_ b_ c_ d_ e_ f_ g_ h_ 579 | , Maybe.map5 580 | (\a8 b8 c8 d8 e8 -> 581 | Maybe.map3 (\f8 g8 h8 -> onSuccess a8 b8 c8 d8 e8 f8 g8 h8) f_ g_ h_ 582 | ) 583 | a_ b_ c_ d_ e_ 584 | |> Maybe.withDefault Nothing 585 | |> toCmd 586 | ) 587 | in 588 | case msg of 589 | LoadedA8 data -> 590 | next (Just data) b c d e f g h 591 | 592 | LoadedB8 data -> 593 | next a (Just data) c d e f g h 594 | 595 | LoadedC8 data -> 596 | next a b (Just data) d e f g h 597 | 598 | LoadedD8 data -> 599 | next a b c (Just data) e f g h 600 | 601 | LoadedE8 data -> 602 | next a b c d (Just data) f g h 603 | 604 | LoadedF8 data -> 605 | next a b c d e (Just data) g h 606 | 607 | LoadedG8 data -> 608 | next a b c d e f (Just data) h 609 | 610 | LoadedH8 data -> 611 | next a b c d e f g (Just data) 612 | 613 | 614 | 615 | {-| -} 616 | update9 : State9 msg a b c d e f g h i -> Msg9 a b c d e f g h i -> ( State9 msg a b c d e f g h i, Cmd msg ) 617 | update9 (State9 onSuccess a b c d e f g h i) msg = 618 | let 619 | next a_ b_ c_ d_ e_ f_ g_ h_ i_ = 620 | ( State9 onSuccess a_ b_ c_ d_ e_ f_ g_ h_ i_ 621 | , Maybe.map5 622 | (\a9 b9 c9 d9 e9 -> 623 | Maybe.map4 (\f9 g9 h9 i9 -> onSuccess a9 b9 c9 d9 e9 f9 g9 h9 i9) f_ g_ h_ i_ 624 | ) 625 | a_ b_ c_ d_ e_ 626 | |> Maybe.withDefault Nothing 627 | |> toCmd 628 | ) 629 | in 630 | case msg of 631 | LoadedA9 data -> 632 | next (Just data) b c d e f g h i 633 | 634 | LoadedB9 data -> 635 | next a (Just data) c d e f g h i 636 | 637 | LoadedC9 data -> 638 | next a b (Just data) d e f g h i 639 | 640 | LoadedD9 data -> 641 | next a b c (Just data) e f g h i 642 | 643 | LoadedE9 data -> 644 | next a b c d (Just data) f g h i 645 | 646 | LoadedF9 data -> 647 | next a b c d e (Just data) g h i 648 | 649 | LoadedG9 data -> 650 | next a b c d e f (Just data) h i 651 | 652 | LoadedH9 data -> 653 | next a b c d e f g (Just data) i 654 | 655 | LoadedI9 data -> 656 | next a b c d e f g h (Just data) 657 | 658 | 659 | {-| Opaque type for storing state of task lists. 660 | -} 661 | type ListState msg a 662 | = ListState (List a -> msg) (List (Maybe a)) 663 | 664 | 665 | {-| Opaque type for updating state of task lists. 666 | -} 667 | type ListMsg a 668 | = ItemLoaded Int a 669 | 670 | 671 | {-| Attempt a list of tasks which will update when all the tasks have finished 672 | or when one fails. Similar to a `Task.sequence` except in parallel. 673 | 674 | type Msg 675 | = DownloadUpdated (Task.Parallel.ListMsg String) 676 | | DownloadFailed Http.Error 677 | | DownloadCompleted (List String) 678 | 679 | fetchNames : ( ListState String, next ) 680 | fetchNames = 681 | attemptList 682 | { tasks = [ fetchFirstName, fetchSecondName, fetchThirdName ] 683 | , onUpdates = DownloadUpdated 684 | , onFailure = DownloadFailed 685 | , onSuccess = DownloadCompleted 686 | } 687 | 688 | -} 689 | attemptList : 690 | { tasks : List (Task x a) 691 | , onUpdates : ListMsg a -> msg 692 | , onSuccess : List a -> msg 693 | , onFailure : x -> msg 694 | } 695 | -> ( ListState msg a, Cmd msg ) 696 | attemptList { tasks, onUpdates, onSuccess, onFailure } = 697 | ( tasks |> List.map (always Nothing) |> ListState onSuccess 698 | , tasks 699 | |> List.indexedMap 700 | (\index task -> 701 | task |> routeTo (onUpdates << ItemLoaded index) onFailure 702 | ) 703 | |> Cmd.batch 704 | ) 705 | 706 | 707 | {-| Call `updateList` inside of your main update function to check if the 708 | tasks have failed or finished. Maintain a copy of the returned state to pass in 709 | on each subsequent `updateList`. This step is required with 710 | [`attemptList`](#attemptList). 711 | 712 | type Msg 713 | = DownloadUpdated (Task.Parallel.ListMsg Actor Http.Error) 714 | | DownloadFailed Http.Error 715 | | DownloadCompleted (List Actor) 716 | 717 | update : Msg -> Model -> ( Model, Cmd Msg ) 718 | update msg model = 719 | case msg of 720 | DownloadUpdated taskMsg -> 721 | let 722 | ( nextTaskState, next ) = 723 | Task.Parallel.updateList model.taskState taskMsg 724 | in 725 | ( { model | taskState = nextTaskState }, next ) 726 | 727 | DownloadCompleted actors -> 728 | ( { model | actorList = actors, Cmd.none ) 729 | 730 | DownloadFailed err -> 731 | ( { model | loadingError = Just err }, Cmd.none ) 732 | 733 | -} 734 | updateList : ListState msg a -> ListMsg a -> ( ListState msg a, Cmd msg ) 735 | updateList (ListState onSuccess items) (ItemLoaded index newItem) = 736 | let 737 | updatedItems = 738 | items 739 | |> List.indexedMap 740 | (\i maybeItem -> 741 | if i == index then 742 | Just newItem 743 | 744 | else 745 | maybeItem 746 | ) 747 | in 748 | if List.any ((==) Nothing) updatedItems then 749 | ( ListState onSuccess updatedItems, Cmd.none ) 750 | 751 | else 752 | ( ListState onSuccess updatedItems 753 | , Just (onSuccess (updatedItems |> List.filterMap identity)) |> toCmd 754 | ) 755 | 756 | 757 | 758 | -- Internal 759 | 760 | 761 | routeTo : (a -> msg) -> (x -> msg) -> Task x a -> Cmd msg 762 | routeTo successMsg failureMsg = 763 | Task.andThen (Task.succeed << Result.Ok) 764 | >> Task.onError (Task.succeed << Result.Err) 765 | >> Task.perform 766 | (\result -> 767 | case result of 768 | Ok a -> 769 | successMsg a 770 | 771 | Err err -> 772 | failureMsg err 773 | ) 774 | 775 | 776 | toCmd : Maybe msg -> Cmd msg 777 | toCmd = 778 | Maybe.map (Task.succeed >> Task.perform identity) 779 | >> Maybe.withDefault Cmd.none 780 | --------------------------------------------------------------------------------