okaryo.log

How to Handle Exceptions in Dart Asynchronous Functions When try-catch Doesn't Work | okaryo.log

How to Handle Exceptions in Dart Asynchronous Functions When try-catch Doesn't Work

    #Dart#Flutter

Introduction

While trying to catch an exception within a Flutter function and map it to an application-specific error, I encountered a situation where the exception was not caught. However, the exception was caught outside the function.

This issue has to do with how try-catch works inside asynchronous functions, and I’d like to share my findings.

Problematic Code

Consider a function like someAsyncService, which calls fetchFromAPI to fetch data and return it directly. To catch any exceptions thrown by fetchFromAPI, I used a try-catch block in someAsyncService.

Future<String> fetchFromAPI() async {
  await Future.delayed(Duration(seconds: 1));
  throw Exception('error api');
}

Future<String> someAsyncService() {
  try {
    return fetchFromAPI();
  } catch (exception) {
    print('someAsyncService: $exception');
    rethrow;
  }
}

void main() async {
  try {
    final result = await someAsyncService();
    print(result);
  } catch (exception) {
    print('main: $exception');
    rethrow;
  }
}

However, when running the code, no output indicates that the exception was caught in someAsyncService.

main: Exception: error api

Solution

The solution to this issue is to await the fetchFromAPI() call inside someAsyncService. The corrected code is as follows:

Future<String> someAsyncService() async {
  try {
    final result = await fetchFromAPI();
    return result;
    // Alternatively, you can also use `return await fetchFromAPI();`
  } catch (exception) {
    print('someAsyncService: $exception');
    rethrow;
  }
}

Now, the expected output is obtained:

someAsyncService: Exception: error
main: Exception: error

Root Cause

The root cause of this issue is that the asynchronous function fetchFromAPI was not awaited, so by the time it threw an exception, someAsyncService had already completed, making it impossible to catch the exception.

The documentation also highlights the importance of awaiting asynchronous code. Essentially, to handle both successful and erroneous results from a Future (asynchronous function), you must await it.

In general, when writing asynchronous code, you should always await a future when it is produced, and not wait until after another asynchronous delay. That ensures that you are ready to receive any error that the future might produce, which is important because an asynchronous error that no-one is awaiting is an uncaught error and may terminate the running program.

Conclusion

Generally, it’s safe to assume that you should await asynchronous code to avoid such issues. However, in functions that don’t use async, forgetting to await won’t produce lint errors, making it easy to overlook. Other solutions might include creating custom lint rules or writing comprehensive tests.


Related Posts
Related Posts
Promotion

This site uses Google Analytics.