diff --git a/src/BackendTask.elm b/src/BackendTask.elm index a35b46aff..da90aafa6 100644 --- a/src/BackendTask.elm +++ b/src/BackendTask.elm @@ -88,6 +88,7 @@ Any place in your `elm-pages` app where the framework lets you pass in a value o import FatalError exposing (FatalError) import Json.Encode +import List.Chunks import Pages.StaticHttpRequest exposing (RawRequest(..)) @@ -175,6 +176,26 @@ resolve = -} combine : List (BackendTask error value) -> BackendTask error (List value) combine items = + items + |> List.Chunks.chunk 100 + |> List.map combineHelp + |> List.Chunks.chunk 100 + |> List.map combineHelp + |> List.Chunks.chunk 100 + |> List.map combineHelp + |> combineHelp + |> map (List.concat >> List.concat >> List.concat) + + +{-| `combineHelp` on its own will overflow the stack with larger lists of tasks + +dividing the tasks into smaller nested chunks and recombining makes `combine` stack safe + +There's probably a way of doing this without the Lists but it's a neat trick to safely combine lots of tasks! + +-} +combineHelp : List (BackendTask error value) -> BackendTask error (List value) +combineHelp items = List.foldl (map2 (::)) (succeed []) items |> map List.reverse diff --git a/src/List/Chunks.elm b/src/List/Chunks.elm new file mode 100644 index 000000000..2534a81ff --- /dev/null +++ b/src/List/Chunks.elm @@ -0,0 +1,51 @@ +module List.Chunks exposing (chunk) + +import Array exposing (Array) + + +{-| Adapted from + +Split a list into chunks of length `n`. + +Be aware that the last sub-list may be smaller than `n`-items long. + +For example `chunk 3 [1..10] => [[1,2,3], [4,5,6], [7,8,9], [10]]` + +-} +chunk : Int -> List a -> List (List a) +chunk n xs = + if n < 1 then + List.singleton xs + + else + evaluate (chunkInternal n xs Array.empty) + + +chunkInternal : Int -> List a -> Array (List a) -> Trampoline (List (List a)) +chunkInternal n xs acc = + -- elm-review: known-unoptimized-recursion + if List.isEmpty xs then + Done (Array.toList acc) + + else + Jump + (\_ -> + chunkInternal n + (List.drop n xs) + (Array.push (List.take n xs) acc) + ) + + +type Trampoline a + = Done a + | Jump (() -> Trampoline a) + + +evaluate : Trampoline a -> a +evaluate trampoline = + case trampoline of + Done value -> + value + + Jump f -> + evaluate (f ())