tgvashworth / fetch-engine

A smart request-making library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Prior Art

passy opened this issue · comments

I think it would be good to collect some prior art in this area. Whether it tries to solve the same problems or a subset of them or tries to tackle them as a secondary concern. Ultimately, this could be part of the design.

Relay

See https://facebook.github.io/relay/docs/guides-network-layer.html.

Relay supports custom network layers. The default one comes with support for CORS, timeouts, retry delays and custom headers (for authorization).

Example:

Relay.injectNetworkLayer(
  new Relay.DefaultNetworkLayer('http://example.com/graphql', {
    fetchTimeout: 30000,   // Timeout after 30s.
    retryDelays: [5000],   // Only retry once after a 5s delay.
  })
);

Angular 1 $resource

Primary concern here is abstracting away JSON APIs, but it comes with some options for timeouts, caching and cancellation. Some interesting notes here about promises and cancellation:

timeout{number} – timeout in milliseconds.
Note: In contrast to $http.config, promises are not supported in $resource, because the same value has to be re-used for multiple requests. If you are looking for a way to cancel requests, you should use the cancellable option.

ancellable{boolean}– if set to true, the request made by a "non-instance" call will be cancelled (if not already completed) by calling $cancelRequest() on the call's return value. Calling $cancelRequest() for a non-cancellable or an already completed/cancelled request will have no effect.
Note: If a timeout is specified in millisecondes, cancellable is ignored.

Ember Data

I couldn't find much about the network layer here, but they were one of the first to use promises, as far as I remember. Might be interesting to see what they've learned from this.

Falcor

Netflix knows a thing or two about distributed systems, I'm sure there's something to learn from them.

They have a separate module for HTTP communication: falcor-http-datasource

Interesting bits worth looking at:

  • Their use of TypeScript.
  • They use Observables rather than Promises and use unsubscribing as means for cancellation.
  • CORS support

Axious

Promise based HTTP client for the browser and node.js

I've been seeing this one a lot lately. Some highlights:

  • axios.all(iterable) and axios.spread(callback) as concurrency helpers
  • transformResponse accepts an array of transformation callbacks
  • Configurable timeouts, but constant.

Affjax

Don't know how I could have forgotten about this one. A PureScript library that runs in the Aff effect monad. Aff comes with instances for Semigroup, Monoid, Apply, Applicative, Bind, Monad, Alt, Plus, MonadPlus, MonadEff, and MonadError which makes super pleasant to work with in error cases and for complex concurrency setups.

Some niceties you get for free thanks to those type classes:

Error handling

(not particularly exciting if you're used to try/catch, but bear in mind that this is all happening asynchronously)

do
  e <- attempt $ Affjax.get "https://phuu.net"
  liftEff $ either (const $ log "Oh noes!") (const $ log "Yays!") e

attempt has type attempt :: forall e a. Aff e a -> Aff e (Either Error a)

Alt

Providing an alternative in case of a failure:

do
  result <- Affjax.get "https://phuu.net" <|> Affjax.get "https://passy.me"
  return result

Forking and cancellation

I still find this stuff super fascinating given that this all runs in a single thread.

canceler <- forkAff myAff
canceled <- canceler `cancel` (error "Just had to cancel")
_        <- liftEff $ if canceled then (log "Canceled") else (log "Not Canceled")

If you want to run a custom canceler if some other asynchronous computation is cancelled, you can use the cancelWith combinator:

otherAff `cancelWith` myCanceler

To me the most interesting take-away for me here is how little the actual Affjax implementation has to do in order to get all those features. Most of them come for free simply by picking the right abstraction to build on top of.

Retry

This feature does not come from Aff, but is implemented in Affjax itself:

type RetryDelayCurve = Int -> Int

You can define your own retry curve and pluck it into a retry policy:

type RetryPolicy = { timeout :: Maybe Int, delayCurve :: RetryDelayCurve, shouldRetryWithStatusCode :: StatusCode -> Boolean }

Those were all I could think of at the top of my head, but I'm sure that there are a bunch more out there.

This is fantastic, thanks @passy. Observables for cancellation is pretty interesting, and something that's been suggested elsewhere.

Adding s3.js since it's inspiration for issue #9 ...

S3 JS

Simple S3 upload with support for progress events

I've added two more to my initial post: anxios and affjax.