reflex-frp / reflex-dom

Web applications without callbacks or side-effects. Reflex-DOM brings the power of functional reactive programming (FRP) to the web. Build HTML and other Document Object Model (DOM) data with a pure functional interface.
https://reflex-frp.org
BSD 3-Clause "New" or "Revised" License
357 stars 145 forks source link

xhr with Dynamic #64

Closed andrewthad closed 8 years ago

andrewthad commented 8 years ago

The xhr functions all work on Events. For example,

performRequestAsync :: MonadWidget t m => Event t XhrRequest -> m (Event t XhrResponse)

It would be nice to have a version of this that worked on Dynamic. The type signature would be:

performRequestAsyncDyn :: MonadWidget t m => Dynamic t XhrRequest -> m (Dynamic t XhrResponse)
mightybyte commented 8 years ago

What would the behavior of this function be?

ryantrinkle commented 8 years ago

I think there's some really interesting potential here:

  1. We can make sure that if responses come back out-of-order, we still guarantee that the one issued last is considered authoritative; I think this represents what is usually wanted (because it ensures that values eventually "settle").
  2. We can automatically cancel requests whose results are no longer needed.
  3. We might want to have a version where the output is Dynamic t (Maybe XhrResponse), so that we can ensure that results are only provided if they are fully up-to-date.

Now, these things can almost certainly be provided independently from the Xhr concept, and possibly from each other. However, I think they'd all be very useful, and it would also be very helpful to make it clear how they can be used with Xhr.

mightybyte commented 8 years ago

@ryantrinkle Wouldn't it have to return a Maybe XhrResponse to handle the times before the first response comes back?

ryantrinkle commented 8 years ago

@mightybyte Good point; yes, it would, or it would need to take an initial value.

andrewthad commented 8 years ago

@mightybyte Ryan's thought's about this are more detailed than mine, but here is the behavior I would expect (regardless of any request cancellation or reordering):

Let's say that somewhere in the codebase, we have the following

resp <- performRequestAsyncDyn req

I would expect that the event updated resp would behave just like an event created by the performRequestAsync that already exists in reflex. Sampling the behavior current resp would only cause an xhr request to fire if it had not previously fired (either as a result of current resp being sampled earlier or as a result of updated req being triggered). So, having this as a Dynamic instead of an Event would only cause an xhr request to fire at most 1 additional time.

I don't know if it would be possible to avoid xhr requests in cases where nothing is listening to updated resp. That's less important in my situation. Let me know if there's anything I can clarify.

ryantrinkle commented 8 years ago

@andrewthad Making a request on sample would be problematic, because it would block the entire FRP computation.

It's definitely possible to avoid requests if updated resp is not being used, but there's a bit of a semantic issue, because, really, updated resp doesn't need to be subscribed until after the Xhr is sent. E.g. if your request event caused updated resp to be subscribed (using switch or similar), it wouldn't be subscribed soon enough to make the XHR get sent.

andrewthad commented 8 years ago

Both of those points makes sense. The first one (about not being able to sample and XHR response), makes me wonder what the most idiomatic solution to a certain problem I am having is.

Basically, I have a dropdown that offers a list of options to use the user. The options are populated by an XHR request. Right now, I am just initializing them to an empty list (or Map, I can't remember at the moment), and then I have a button that I click to populate them. It is conceivable that the available options could change after the page loads, so the ability to read load them with this button is useful. However, it would be better if this drop-down could be populated right after the page loads as well. Otherwise, the end-user always needs to hit the reload button for the drop-down after they load the page.

That's the most simple example of this problem I can think of. I have run into similar situations where I want to fire an XHR request based on a dynamic that is created by holdDyn. it would be nice to have a way to get an XHR requests to fire not just for all of the updates to the dynamic but also for the initial value of it. It's possible that I am engaging in some sort of anti-pattern by getting into the situations though.

Sent from my iPhone

On Jun 14, 2016, at 5:22 PM, Ryan Trinkle notifications@github.com wrote:

@andrewthad Making a request on sample would be problematic, because it would block the entire FRP computation.

It's definitely possible to avoid requests if updated resp is not being used, but there's a bit of a semantic issue, because, really, updated resp doesn't need to be subscribed until after the Xhr is sent. E.g. if your request event caused updated resp to be subscribed (using switch or similar), it wouldn't be subscribed soon enough to make the XHR get sent.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

ryantrinkle commented 8 years ago

@andrewthad Can you use the Event returned by getPostBuild for that purpose?

andrewthad commented 8 years ago

Sorry I forgot to respond to this. Yes, getPostBuild ended up giving me what I needed.

ryantrinkle commented 8 years ago

Awesome!