react-native: AbortController is missing

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?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 17
  • Comments: 31 (7 by maintainers)

Commits related to this issue

Most upvoted comments

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 ☺️

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

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

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

tks

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.--*/
}

Same problem.

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

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
	})
}

Is there any update about this?


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

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

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;
  1. use signal from step 5) in your fetch fetch(url, {signal}). This fetch will be from cross-fetch

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();
  }
}
$ npm ls whatwg-fetch
└─┬ react-native@0.57.0
  └─┬ fbjs@0.8.17
    └─┬ isomorphic-fetch@2.2.1
      └── whatwg-fetch@3.0.0

@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.

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.