gleam-lang / fetch

📡 Make requests to HTTP servers with fetch
Apache License 2.0
33 stars 8 forks source link

Making requests with timeout #10

Open johtso opened 2 weeks ago

johtso commented 2 weeks ago

Specifying a timeout when making a web request seems like a core feature, otherwise your code is at the whim of whatever you're communicating with.

Usually I'd pass a AbortSignal.timeout(time) in the fetch options.

Another solution would be to race the promise with setTimeout. Doesn't look like the promises package exposed race functionality though.

Possibly linked with #4.. depending on the approach

Related: https://github.com/gleam-lang/javascript/issues/11 https://github.com/gleam-lang/javascript/issues/10

lpil commented 2 weeks ago

Good idea. What might the API be?

johtso commented 2 weeks ago

Good idea. What might the API be?

Possibly gleam/javascript or plinth should expose AbortController/AbortSignal, and then if this package supported passing options with the request then we could just do that.

That would basically just be replicating the javascript way of doing it though.

Not sure if we want to expose friendlier APIs, or just stick with the most javascripty way.

lpil commented 2 weeks ago

For any package I maintain all APIs must be designed with Gleam in mind, in order to get the best experience for the language.

johtso commented 2 weeks ago

Okay, well, in that case I guess the first question is what functionality we want to expose.

At its simplest, javascript's signal approach gives you a function that you can call to abort the request. AbortSignal.timeout(ms) just gives you a signal that is automatically aborted after that time.

You can also combine signals using AbortSignal.any(...signals), for example if you want a timeout, but also the ability to explicitly abort the request.

Not sure what you think about this API suggestion for manipulating options? https://github.com/gleam-lang/fetch/pull/5

Based on that, my first thought was something like:

let #(abort, options) = fetch.with_abort_signal(options)
fetch.with_timeout(options, 5000)

But I guess that would be awkward when it comes to chaining options functions, seeing as one wants to give you the abort function as well as the new options.

It's also a little tricky as it's not just an array of signals we can append to, they all need to be combined in one go using AbortSignal.any.

But I guess we could keep the signals in an array and then bundle them up when sending the request.. or..

...alternatively we do something like:

let #(abort, signal1) = fetch.abort_signal()
let signal2 = fetch.timeout_signal(5000)

fetch.with_signals(options, [signal1, signal2])

// and maybe a shortcut
fetch.with_timeout(options, 5000)

Anyway, that's what my gleam-novice brain managed to squeeze out 🍋

https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal https://developer.mozilla.org/en-US/docs/Web/API/AbortController

lpil commented 2 weeks ago

What are some real-world use cases here? If we gather those we can see what functionality folks are largely using today in JS.

johtso commented 2 weeks ago

@lpil real world use cases are a great place to start!

Timeouts:

Abort signals:

Combined abort signals: