Retrying failed requests... without the headache

This is kind of diametrically opposed to the API that @1hko is interested in

I think this generalization is an improvement! I’m suggesting rename of retryHelp to onError as I think it makes the complete module read nicer. Hmm :thinking:

Given your two implementations above:

retry : Config -> Task x a -> Task x a
onError : Config -> Task x a -> x -> Task x a

We can rewrite Retry.sendWith

sendWith : Config -> (Result Http.Error a -> msg) -> Http.Request a -> Cmd msg
sendWith config resultToMessage req =
    let
        task =
            Http.toTask req
    in
        task
            |> Task.onError (onError config task)
            |> Task.attempt resultToMessage

Now we have a more generic Retry module that can retry tasks. And in a more specialized case, it can retry Http requests. We can expose the generic too :star2:


:star: Revision 3

@ericgj simplifies the retry abstraction and builds a more generic api, making the entire module more useful.

-- module Retry
default : Config
retry : Task x a -> Task x a
retryWith : Config -> Task x a -> Task x a
send : (Result Http.Error a -> msg) -> Http.Request a -> Cmd msg
sendWith : Config -> (Result Http.Error a -> msg) -> Http.Request a -> Cmd msg

Implementation

-- revision 3
module Retry exposing (Config, default, retry, retryWith, send, sendWith)

import Http
import Process
import Task exposing (Task)
import Time exposing (Time)


type alias Config =
    { retries : Int
    , interval : Time
    }


default : Config
default =
    { retries = 5
    , interval = 1 * Time.second
    }


retry : Task x a -> Task x a
retry =
    retryWith default


retryWith : Config -> Task x a -> Task x a
retryWith config task =
    task |> Task.onError (onError config task)


send : (Result Http.Error a -> msg) -> Http.Request a -> Cmd msg
send =
    sendWith default


sendWith : Config -> (Result Http.Error a -> msg) -> Http.Request a -> Cmd msg
sendWith config resultToMessage req =
    let
        task =
            Http.toTask req
    in
        task
            |> Task.onError (onError config task)
            |> Task.attempt resultToMessage


onError : Config -> Task x a -> x -> Task x a
onError config task error =
    if config.retries == 0 then
        let
            _ =
                Debug.log "failed retrying" error
        in
            Task.fail error
    else
        let
            _ =
                Debug.log ("retrying " ++ (toString config.retries)) error

            next =
                task
                    |> Task.onError (onError { config | retries = config.retries - 1 } task)
        in
            Process.sleep config.interval
                |> Task.andThen (always next)

Probably time to learn how to publish packages… :package:

3 Likes