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

Feature Request: abort request with AbortController signal #187

Open
wachunei opened this issue May 30, 2018 · 9 comments
Open

Feature Request: abort request with AbortController signal #187

wachunei opened this issue May 30, 2018 · 9 comments

Comments

@wachunei
Copy link

Most probably not a small set of developers had run into wanting to abort an ongoing fetch call. For instance an autocomplete input that makes requests as key events are happening, even with a debouncer.

Now in most browsers AbortController API is available in order to fulfill this task.

This issue is an open question for the community if there's interest in including this feature and what we could achieve. I think an extension of RSAA is needed at least.

What do you guys think? I could definitely set up some code proposals or tests in the next days if this idea gains traction 😃

@nason
Copy link
Collaborator

nason commented Jun 7, 2018

Love it!

My first question is whats the API? You invoke a fetch call by dispatching a RSAA, how would cancelling that fetch later happen?

@wachunei
Copy link
Author

wachunei commented Jun 14, 2018

Big question... have not thought about it yet 😅

@iamandrewluca
Copy link
Collaborator

iamandrewluca commented Jul 5, 2018

@nason @wachunei I see it this way,

const actionCreator = (controller, abortCtrl1, abortCtrl2) => ({
    [RSAA]: {
        // ...
        types: ['REQUEST', 'SUCCESS', 'FAILURE', 'ABORT']
        abort: [abortCtrl1, abortCtrl2],
        signal: controller.signal
        // ...
    }
})

when REQUEST was started, abort abortCtrl1, abortCtrl2 from abort
watch for abort event on signal, cancel all requests and dispatch ABORT

@iamandrewluca
Copy link
Collaborator

And maybe some global signal can be useful

@darthrellimnad
Copy link
Contributor

darthrellimnad commented Jul 12, 2018

I like this idea! But would we really need a new Action Type to RSAA spec? According to MDN docs, an aborted fetch should reject the promise, meaning it could produce a normal Failure action, with a specific error type?

If that's the case, the RSAA would only need to be given the custom signal option. That way, the feature dispatching the RSAA would need to maintain a reference to the controller in order to abort the request, which seems more a responsibility of an Action Creator util method or app-specific middleware (or epic or saga, etc), and not redux-api-middleware itself.

@darthrellimnad
Copy link
Contributor

darthrellimnad commented Jul 12, 2018

And actually, thinking on this more, could we use the existing options RSAA property to supply the signal already? Would only work if browser supports it, or we supply our own polyfill though... afk ATM but I might try this out a bit on my end too :)

@wachunei
Copy link
Author

I think we should not polyfill, just like fetch.

@darthrellimnad
Copy link
Contributor

darthrellimnad commented Jul 12, 2018

So I tested this out with native fetch, and I think this kinda works already 🎉

I still need to play with this more and test... but I was able to "abort" an RSAA generated fetch request, by utilizing the existing options hash supported by the RSAA spec (in current 2.3.0, but likely would work in 3.0.0 as well?).

Here's what I did to "abort" a network request that was initiated by redux-api-middleware:

// example case for basic action creator
const myRsaaCreator = (rsaaProps) => {
  const controller = new window.AbortController()
  const { signal } = controller
  const rsaa = {
    [RSAA]: {
      // ...
      ...rsaaProps,
      options: { signal }
    }
  }
  return [ rsaa, controller ]
}

// then later dispatch...
const [ rsaa, controller ] = myRsaaCreator()
dispatch(rsaa)

// then later abort...
controller.abort()

tricky part is figuring out where you need access to this controller, so unlike my above example, may not always make sense to tightly couple 1 controller to 1 rsaa. A more realistic example is that you may have some type of "context change" that you want to issue an "abort" signal to, so that any and/or all pending network requests can be closed to free up per-domain channels. In this case, maybe you want another middleware to manage applying a single controller's signal to dispatched RSAAs dynamically and issuing an abort() if a certain action is dispatched, or app event occurs later on :)

@nason I think maybe this just needs to be tested a bit more and documented in v 3.0.0? I can help out with this is you feel there needs to be additional changes, since it may help to better handle the "error" message and document behavior... here's the error action payload I received when I "aborted" an rsaa fetch:

{
    name: 'RequestError',
    message: 'The user aborted a request.'
}

In v 3.0.0, this would be a Failure action, but might help if we can better distinguish an "abort" failure from other types of failures?

@KrzysztofMadejski
Copy link

KrzysztofMadejski commented Nov 19, 2021

@darthrellimnad I think we started using that approach in our codebase and I just recently figured out that on abort SUCCESS action is being called with payload = error InternalError: The user aborted a request. Do you see any workaround to have FAILURE called? Some kind of wrapper that checks returned payload before issuing an action? I wouldn't like to have error handling in each SUCCESS action handling code..

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

No branches or pull requests

5 participants