Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Solving Unhandled Promise Rejection in React Native

Solving Unhandled Promise Rejection

~9 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

"Possible Unhandled Promise Rejection" is one of the most common warnings in React Native apps – it appears when a Promise fails ("rejects") but NO .catch() or try/catch handles that error.

1. Description

EVERY async function implicitly returns a promise, and every await call can fail (network error, invalid JSON response, denied permission). If the error stays unhandled, React Native logs a warning – in the worst case, the app gets stuck in an unexpected state (e.g. a loading spinner that never disappears).

2. Short example

// WRONG: no catch, the error silently vanishes
async function load() {
  const response = await fetch(url); // throws on network error
}

// RIGHT:
async function load() {
  try {
    const response = await fetch(url);
  } catch (error) {
    console.error(error);
  }
}

3. Complete project: robust data loading

npx create-expo-app unhandled-rejection-demo
cd unhandled-rejection-demo
App.js
import { useState, useEffect } from 'react';
import { View, Text, Button, ActivityIndicator, StyleSheet } from 'react-native';

async function loadQuote(shouldFail) {
  const response = await fetch(
    shouldFail ? 'https://invalid-address.example/nothing' : 'https://api.quotable.io/random'
  );
  if (!response.ok) {
    throw new Error(`Server responded with status ${response.status}`);
  }
  return response.json();
}

export default function App() {
  const [quote, setQuote] = useState(null);
  const [loading, setLoading] = useState(false);
  const [errorMessage, setErrorMessage] = useState(null);

  async function reload(shouldFail = false) {
    setLoading(true);
    setErrorMessage(null);
    // try/catch handles BOTH network errors AND the manual throw above -
    // without this try/catch, every failure case would become an unhandled rejection
    try {
      const data = await loadQuote(shouldFail);
      setQuote(data.content);
    } catch (error) {
      console.error('Failed to load quote:', error.message);
      setErrorMessage('Failed to load quote. Please try again.');
    } finally {
      // finally ALWAYS runs, success or failure - loading state is guaranteed to end
      setLoading(false);
    }
  }

  useEffect(() => {
    reload();
  }, []);

  return (
    <View style={styles.container}>
      {loading && <ActivityIndicator />}
      {quote && <Text style={styles.quote}>"{quote}"</Text>}
      {errorMessage && <Text style={styles.error}>{errorMessage}</Text>}
      <View style={styles.buttons}>
        <Button title="New quote" onPress={() => reload(false)} />
        <Button title="Simulate error" onPress={() => reload(true)} color="#dc2626" />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 100, paddingHorizontal: 24 },
  quote: { fontSize: 16, fontStyle: 'italic', marginBottom: 20 },
  error: { color: '#dc2626', marginBottom: 20 },
  buttons: { gap: 12 },
});

4. Explanation

  • EVERY await call that CAN fail sits inside a try block – that's the only reliable way to avoid unhandled rejections in async functions.
  • finally guarantees setLoading(false) is ALWAYS called – a very common bug without finally: the loading spinner stays visible forever on an error, because the reset in the success path is "forgotten" and never reached on the error path.
  • The manual throw new Error(...) on a non-successful HTTP status is needed because fetch() itself (unlike Axios) does NOT automatically reject on 4xx/5xx responses (see the "Sending a POST Request" chapter).
  • In a production app, you'd additionally register a global fallback (e.g. via ErrorUtils.setGlobalHandler()) that at least centrally logs TRULY missed errors – a last safety net, not a replacement for local try/catch blocks.

5. Outputs

Ausgabe
On start, a loading spinner briefly appears, then an italic quote in quotation marks. Two buttons below: "New quote" loads a new random quote, "Simulate error" deliberately calls an invalid URL and shows red text "Failed to load quote. Please try again." instead — in NO case does the loading spinner get stuck or an unhandled rejection warning appear in the terminal.