facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
118.55k stars 24.28k forks source link

AbortSignal, TypeError: AbortSignal.timeout is not a function #42042

Open HenrikZabel opened 9 months ago

HenrikZabel commented 9 months ago

Description

I want to abort a fetch request after 5 seconds. I am using following code:

const getStatus = () => {
  fetch(`${URL}/api/status`, {
    signal: AbortSignal.timeout(5000)
  }).then(response => {
    console.log(response)
  })
  .catch(err => {
    console.error(err.name, err.code, err.message, err.cause);
  })
};

Steps to reproduce

  1. Install React-Native
  2. Install newest node version

React Native Version

0.73.1

Affected Platforms

Runtime - Android

Output of npx react-native info

System:
    OS: Windows 10 10.0.19045
    CPU: (6) x64 Intel(R) Core(TM) i5-9600K CPU @ 3.70GHz
    Memory: 1.03 GB / 15.93 GB
  Binaries:
    Node: 21.5.0 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 8.3.0 - ~\AppData\Roaming\npm\npm.CMD
    Watchman: Not Found
  SDKs:
    Android SDK: Not Found
    Windows SDK:
      AllowAllTrustedApps: Disabled
  IDEs:
    Android Studio: AI-212.5712.43.2112.8609683
    Visual Studio: Not Found
  Languages:
    Java: 17.0.8
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.1.0 => 18.1.0
    react-native: 0.73.1 => 0.73.1
    react-native-windows: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Stacktrace or Logs

TypeError: AbortSignal.timeout is not a function (it is undefined), js engine: hermes

Reproducer

https://github.com/HenrikZabel/abortsignal-bug/

Screenshots and Videos

No response

RITE-Soft commented 7 months ago

I also encountered this today.

Edit: It seems that RN just doesnt work with AbortSignal.timeout (yet), maybe its due to hermes idk but I did create a drop-in replacement for fetch that implements timeout as part of the native options

export const fetch = async (
  url: string,
  { timeout = 5000, ...fetchOptions }: RequestInit & { timeout?: number } = {}
) => {
  const controller = new AbortController();

  const abort = setTimeout(() => {
    controller.abort();
  }, timeout);

  const response = await globalThis.fetch(url, {
    ...fetchOptions,
    signal: controller.signal,
  });

  clearTimeout(abort);
  return response;
};

Note that this will just throw an AbortError, if you want any more control over the exception thrown, make sure to wrap the fetch with a try catch and handle the exceptions accordingly

jsg2021 commented 18 hours ago

The same goes for AbortSignal.any(). :(