React-native: AbortController is missing

Created on 27 Feb 2018  Â·  31Comments  Â·  Source: facebook/react-native

Is this a bug report?

Yes. AbortController is a new spec (you can find info here: https://developers.google.com/web/updates/2017/09/abortable-fetch)

Have you read the Contributing Guidelines?

Yes

Environment

Environment:
  OS: Linux 4.13
  Node: 8.9.4
  Yarn: 1.3.2
  npm: 5.6.0
  Watchman: Not Found
  Xcode: N/A
  Android Studio: Not Found

Packages: (wanted => installed)
  react: ^16.0.0 => 16.2.0
  react-native: ^0.53.0 => 0.53.0

Steps to Reproduce

  1. Istantiate an AbortController
  2. Write a fetch request passing the signal
  3. Try to abort the fetch request

Expected Behavior

The requested behaviour is to abort the fetch request.

Actual Behavior

Nothing happens

Reproducible Demo

Something like this:

const controller = new AbortController();
const signal = controller.signal;

setTimeout(() => controller.abort(), 5000);

fetch(url, { signal }).then(response => {
  return response.text();
}).then(text => {
  console.log(text);
});

My question is a technical one: is the react native core that needs to be updated in order to support this? Or is it something that babel can patch with a new version?

Bug JavaScript Linux Locked

Most helpful comment

Great, it works!
I'm not on a create react app, but I've just followed the instruction for it (I'm actually on a react-native project not even on a create react native app

So installed the module, imported it like this:

import 'abortcontroller-polyfill';

and then used it like this:

const AbortController = window.AbortController;
const controller = new AbortController()
const signal = controller.signal

I'd not close the issue, because we're still waiting for the official spec.
Thank you @chirag04 for linking me the right resource :relaxed:

All 31 comments

Having same issue. This should be in react-native too.

might be worth waiting for https://github.com/github/fetch/pull/572 to merge.

Very good, at least I should be able to use this polyfill right now in react native right?

I haven’t used it but should be able to use it. Might be worth posting here what you find out

Great, it works!
I'm not on a create react app, but I've just followed the instruction for it (I'm actually on a react-native project not even on a create react native app

So installed the module, imported it like this:

import 'abortcontroller-polyfill';

and then used it like this:

const AbortController = window.AbortController;
const controller = new AbortController()
const signal = controller.signal

I'd not close the issue, because we're still waiting for the official spec.
Thank you @chirag04 for linking me the right resource :relaxed:

@giacomocerquone the polyfill is working on react-native (https://github.com/facebook/react-native/issues/18115#issuecomment-368978604)

do u have an example of it?

Just doing what I wrote in the previous comment should be fine. All the code then would be equals to the one used in the browsers like the piece showed on mozilla doc: https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort

I've actually a production app where I'm using this. I can't share directly the code of course, but I can re-arrange an example if it is strictly needed :)

it works, but won't really cancel the request, it will just throw an exception

tks

Basically it just rejects the promise.
It's true that it won't cancel the request (we should also specify precisely what we mean when we write 'cancel'), but as far as I'm concerned, my interest was to not let the request hanging and to 'close the door' to all the responses that would have come after the abort call.

Hey there, it looks like there has been no activity on this issue recently. Has the issue been fixed, or does it still require the community's attention? This issue may be closed if no further activity occurs. You may also label this issue as "For Discussion" or "Good first issue" and I will leave it open. Thank you for your contributions.

I just tried it today with the polyfills, and it doesn’t work.

Is there any update about this?



@giacomocerquone It would be nice if you can provide an example to understand better how it works 😄

Any update on this?

AbortController + signal imports and fires, but the fetch is not interrupted. I tried @giacomocerquone 's resolution, but that didn't abort the fetch either. Is there another polyfill i'm missing?

function api(endpoint, method, body, contentType = 'application/json') {

    const controller = new AbortController()
    const signal = controller.signal
    setTimeout(() => controller.abort(), 500)

    let data = {
        signal,
        method: method,
        headers: {
            'Content-Type': contentType
        } 
    }

    if (body) {
        data['body'] = JSON.stringify(body)
    }

    return fetch(`${API_URI}/${endpoint}`, data)
    .then(response => {
        if (!response.ok) {
            throw Error(response.statusText)
        }

        return response.json()
    })
    .then(response => {
        return response.data
    })
    .catch(error => {
        console.log('API request failed:', error.message)

        return false
    })
}

Same problem.

Try to cancel fetch after timeout (5 sec), for simulating bad connection I'm using Network link conditioner

The abortcontroller-polyfill works

A code snippet:

import "abortcontroller-polyfill";

class App extends React.Component {
    constructor() {
        const AbortController = window.AbortController;
        this.controller = new AbortController();
        this.signal = this.controller.signal;
    }
    componentDidMount() {
        const url = "https://myurl.com";
        const signal = this.signal;

        fetch(url, { signal })
        .then(res => res.json())
        .then(json => console.log(json))
        .catch(err => {
            // You can catch the error
            // thrown by the polyfill here.
            if (err.name == "AbortError") {
                console.log("Fetch Aborted");
            } else {
               //Catch other errors.
            }
        });
    }
    componentWillUnmount() {
        this.controller.abort();
    }
    /*--Rest of the code.--*/
}

@samueladekunle you're using window.AbortController... is that intended?
In my case (using react-native v0.54.4) the abortcontroller's polyfill only works if I use its internal abortableFetch and AbortController implementations. Why? Because the polyfill works like this: if it founds a fetch object, it uses it. Otherwise, it uses own implementation.

So, to put it simply:

  • v0.54 uses whatwg-fetch if present (https://github.com/facebook/react-native/blob/0.54-stable/Libraries/Network/fetch.js#L19)
  • whatwg-fetch does not implement AbortController whatsoever and its fetch implementation is not compliant with the new spec (at least, v1.0.0 which is the one RN 0.54.4 uses).
  • abortcontroller-polyfill is implementing the AbortController stuff but if your code is using the fetch from whatwg-fetch` it's not gonna work. Hence, you need to use the polyfill's fetch.

Apparently, this issue should not happen with react-native 0.57 since whatwg-fetch was remove with this commit but I'm not 100% sure.

$ npm ls whatwg-fetch
└─┬ [email protected]
  └─┬ [email protected]
    └─┬ [email protected]
      └── [email protected]

I think the polyfill works as expected on RN 0.57. But sometimes, the abort event is received after the fetch promise has already resolved and won the Promise.race.
Probably because of using setTimeout that places the dispatch event on a queue.

One way to get around this is checking for controller.signal.aborted flag if you are no longer interested in the response. (e.g. the component is unmounted)

How do you solve the jest mocking when using @giacomocerquone solution?
image

I've tried to mock AbortController like below and won't work.

export default class AbortController {
  constructor() {
    this.controller = new AbortController();
  }
}

Apparently, this issue should not happen with react-native 0.57 since whatwg-fetch was remove with this commit but I'm not 100% sure.

The polyfill in that commit does not contain any logic for Abort controllers so I do not think it works on latest rn as well (since the vendored file is based on 1.0.0 of whatwg-fetch).

Any updates of this?
Have same problem. I have tried to use new version of whatwg-fetch 3.0.0 with Aborting fetch, and it's work but, when we create new build it will crash. Because they change code in 3.0.0 for browsers.

@chmartinez can you give example how we can you abortableFetch from abortcontroller-polyfill

So, I try to use cross-fetch instead latest version of whatwg-fetch

Upd. Looks like it is working. And what I have done:
1) Install cross-fetch
2) Install abortcontroller-polyfill for using AbortController()
3) import fetch like import fetch from 'cross-fetch';
4) import abortcontroller-polyfill like import "abortcontroller-polyfill"
5) create signal for fetch:

const AbortController = window.AbortController;
this.controller = new AbortController();
this.signal = this.controller.signal;

6) use signal from step 5) in your fetch fetch(url, {signal}). This fetch will be from cross-fetch

I wrote an article for all the people that were asking me to shed some light about this matter: https://blog.giacomocerquone.com/aborting-fetch-react-native/

In the next one I will give you some ready to use code to reject requests, but this time instead of giving you code I wanted to explain what is going on and how and why is useless to use polyfills in react native as of now.

Enjoy, hope I did a good thing!
As I wrote in the post, in the next article I will share with you my simple and short piece of code that I use to make requests that implements fetch rejection with few lines of code among other things

Seems like work on AbortController for RN was already done.
https://github.com/facebook/react-native/commit/5e36b0c6eb2494cefd11907673aa018831526750

@atkit Yes, work on this has been done, however, a comment on the issue related to this merge says that this work is focussed towards RN 0.60+, hence not support on previous versions.

@raunaqss as I've already said, an implementation from facebook was needed in order to make an abortion work, hence old version cannot be supported. Why the downvote?

Support for this was included in the new version; 0.60, which was released yesterday.

https://github.com/react-native-community/releases/blob/master/CHANGELOG.md#060

@JordanRichards-hx
I just created a fresh Rn project (0.60.5) with typescript. I see nothing added to fetch's RequestInfo and RequestInit interfaces. is the typescript definition files outdated or we must use RN's fetch API differently? I just confused about how I use the feature.

I found an example here:
https://github.com/facebook/react-native/commit/5e36b0c#diff-3e169de4973928b8c4e90923cac0651aR22

And I checked that it is possible to create instance of AbortController but yes @types/react-native still have no information about AbortController so typescript shows an error. I think it is good opportunity to contribute to @types/react-native (type definitions could be found here https://github.com/mysticatea/abort-controller/blob/master/dist/abort-controller.d.ts)

Is AbortController an RN built-in class? or I should install it from abort-controller repository

I'm asking because of the typescript issue. maybe it's possible to export a utility function to create it for the user and so there is no need to install the lib in the main project

As I can see from codebase it is installed as dependancy to react-native, but typing information is not yet in place for some reason. Typing information lives outside of react-native in repo for all typescript types. As it is a global class for React Native this typing needs to be provided.

I did not start using this AbortController in my apps yet, but I'm planning to implement timeout and cancel operations at some point. I checked quickly it does work and it cancels operation with special type of Error.

Was this page helpful?
0 / 5 - 0 ratings