Camillo Bucciarelli Camillo Bucciarelli
Current language: English. Switch to italiano Current language: English. Switch to italiano
Home Blog Talks CV Contact
← All articles

Error Management and Beyond: A Smart Way to Handle Asynchronous Contexts

Camillo Bucciarelli
Camillo Bucciarelli
Error Management and Beyond: A Smart Way to Handle Asynchronous Contexts

In this article we will talk about how to manage errors so that we can intercept them and act accordingly through specific callbacks.

An example could be that of an API call in which we are asked to manage 3 states:

  1. Loading
  2. Success
  3. Failure

Furthermore, very often in Flutter each of these states corresponds to a UI state which specifically describes to the user what is happening; we will show a loader while we wait for the server’s response, the values ​​obtained in case of a positive response and an error message in case we cannot get the information.

Below we will see a simple way to manage all this in a few lines, both in the presence of a single future value (Future) and for a stream of values (Stream)

Error handling in case of Future

First of all we can create a function that allows us to call our method in a “safe” way and takes care of managing any exceptions, let’s say something like this:

typedef FailureFromExceptionFunction = Failure Function(dynamic e);
typedef FutureOperationFunction<T> = Future<T> Function();

Future<T> runSafetyFuture<T>(
  FutureOperationFunction<T> operation, {
  FailureFromExceptionFunction? onException,
}) async {
  try {
    return await operation();
  } catch (e) {
    return Future.error(onException?.call(e) ?? e);
  }
}

At this point we can use this function to invoke any asynchronous method or function safely without worrying about exceptions.

Ok, yes, fine, but now? How do I define what to do in the various states? I would say we could do it through an extension on Future like the following:

typedef SuccessCallback<T> =  void Function(T);
typedef FailureCallback = void Function(dynamic);

extension FutureExtension<T> on Future<T> {
  Future<void> when({
    VoidCallback? progress,
    SuccessCallback<T>? success,
    FailureCallback? failure,
  }) async {
    progress?.call();
    await then((value) => success?.call(value)).catchError(onErrorHandler(failure));
  }
}

As we can see in the previous code we have a new when method that accepts 3 callbacks, one for each state:

  1. VoidCallback? progress which is invoked immediately and can be used to manage a possible loading state;
  2. SuccessCallback<T> success which is invoked in case we get the data correctly and the Future is resolved correctly;
  3. FailureCallback? failure which is invoked if runSafetyFuture catches an exception.

Error handling in case of Stream

In a similar way to how it happens for Futures we can create our function which has the task of intercepting any exceptions and allowing us to manage them:

typedef StreamOperationFunction<T> = Stream<T> Function();
typedef FailureFromExceptionFunction = Failure Function(dynamic e);

Stream<T> runSafetyStream<T>(
  StreamOperationFunction<T> operation, {
  FailureFromExceptionFunction? onException,
}) {
  return operation().handleError((e) {
    throw onException?.call(e) ?? e;
  });
}

And also in this case we create an extension for managing callbacks:

typedef SuccessCallback<T> =  void Function(T);
typedef FailureCallback = void Function(dynamic);

extension StreamExtension<T> on Stream<T> {
  void when({
    VoidCallback? progress,
    SuccessCallback<T>? success,
    FailureCallback? failure,
  }) {
    progress?.call();
    handleError(onErrorHandler(failure)).forEach((value) => success?.call(value));
  }
}

Also in this case we have the when method which accepts 3 callbacks, one for each state:

  1. VoidCallback? progress which is invoked immediately and can be used to manage a possible loading state;
  2. SuccessCallback<T> success which is invoked in case we get the data correctly and the Future is resolved correctly;
  3. FailureCallback? failure which is invoked if runSafetyFuture catches an exception.
Camillo Bucciarelli
Camillo Bucciarelli
Technical Leader · Flutter dev · Speaker. I write about code and how to survive a team.
Write me