Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to explicitly cancel XHR requests #66

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

EugeneN
Copy link
Contributor

@EugeneN EugeneN commented Sep 10, 2016

Ability to cancel pending [XHR] requests from the business logic is important in real-world applications.

The most basic example is an autocomplete functionality. It continuously issues XHR requests while a user is typing a search term, aggregating input into some chunks. It might happen that a request issued earlier will arrive later than a request with a more recent input, overwriting displayed results.

One could handle this by using some monotonic request ids, but this would complicate things a quite lot.

Also, freeing unneeded resource is a good practice, especially important in a browser. Browsers have a limit on number of simultaneous connections to the same origin as low as 6. Should a user type fast, and should requests stall for a bit, connections limit would be exhausted, which would cause kind of denial-of-service by the browser, degrading user experience, interrupting other functionality etc.

So for business logic to be able to cancel requests it would need a request handle. This PR adds a family of xhr*' functions (xhr', xhrByteString', xhrText', xhrString') which take an XHR object handle as a parameter, and exports 2 existing functions for creating and aborting XHRs (xhrCreate, xhrAbort).

So the low-level API looks like this:

do
  handle <- xhrCreate
  forkIO $ xhr’ handle …
  ...
  when condition $ xhrAbort handle

The API could be implemented in a more advanced way, but it is a low-level API after all :-)

@EugeneN
Copy link
Contributor Author

EugeneN commented Sep 10, 2016

Couple of screenshots illustrating the problem and xhr abort feature:

Before - a request issued earlier finishes much later than the next one:
image

After - a request issued earlier is cancelled before issuing the next one:
image

@luite
Copy link
Member

luite commented Sep 10, 2016

what's the advantage of this new api over the following:

f = do
  tid <- forkIO (xhr ... >>= doSomethingWithResult)
  ...
  when abortCondition (killThread tid)

this aborts the request if necessary

@EugeneN
Copy link
Contributor Author

EugeneN commented Sep 10, 2016

well, it has no advantage if the resources are released (XHRs cancelled), indeed. And this would allow to keep API surface smaller.

Theoretically, one could want to still have a way to operate on XHR-requests-level, if threads orchestration in complex business logic would be too hard or interfering with other logic, or produced some overhead.

For my case threads solution should be enough though. Feel free to close the pr then :-)

@EugeneN
Copy link
Contributor Author

EugeneN commented Sep 12, 2016

Btw, another difference is that forkIO solution would require manual threads synchronisation using MVars or more advanced techniques, while xhr' solution does all the async action under the hood while looking completely synchronous to the app's code (or is there a way of doing such a thing with threads (not counting foreign import interruptible wrappers over Haskell functions, of course)?).

While the sync task is completely doable it could still be convenient to think about XHRs as a primitive synchronous operation which can be used with no extra work.

It would decrease API clear-ness probably. On the other hand, it's web programming, XHRs are omnipresent - maybe it's worth having richer low-level API for them?

@EugeneN
Copy link
Contributor Author

EugeneN commented Sep 13, 2016

Or maybe the best solution would be to just export doRequest, xhrCreate, xhrAbort functions and XHR type.

doRequest is not a new function, but a lambda extracted from existing xhr function: https://github.com/EugeneN/ghcjs-base/blob/c18c2702cd0e5a6b5ad2f8c997b56b126119398c/JavaScript/Web/XMLHttpRequest.hs#L134-L178

In this way no new code is added at all, just few existing primitives exported, so that users could implement any desirable API to XHRs should the default option (which could be easily reconstructed from exported primitives: xhr req = xhrCreate >>= \x -> doRequest req x onException xhrAbort x) fail to satisfy their demands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants