Async<T extends Object> constructor

Async<T extends Object>(
  1. @mustBeAnonymous @mustAwaitAllFutures Future<T> mustAwaitAllFutures(), {
  2. @noFutures TOnErrorCallback<T>? onError,
  3. @noFutures TVoidCallback? onFinalize,
})

Creates an Async by executing an asynchronous function mustAwaitAllFutures.

IMPORTANT:

Always all futures witin mustAwaitAllFutures to ensure errors are be caught and propagated.

Implementation

factory Async(
  @mustBeAnonymous
  @mustAwaitAllFutures
  Future<T> Function() mustAwaitAllFutures, {
  @noFutures TOnErrorCallback<T>? onError,
  @noFutures TVoidCallback? onFinalize,
}) {
  assert(
    isSubtype<T, Never>() || !isSubtype<T, Future<Object>>(),
    '$T must never be a Future.',
  );
  return Async.result(() async {
    Result<T> result;
    try {
      result = Ok<T>(await mustAwaitAllFutures());
    } on Err catch (err) {
      result = err.transfErr<T>();
    } catch (error, stackTrace) {
      if (onError == null) {
        result = Err<T>(error, stackTrace: stackTrace);
      } else {
        try {
          result = onError(error, stackTrace);
        } on Err catch (err) {
          // `onError` itself can throw an `Err` — preserve its statusCode
          // and breadcrumbs rather than nesting it as another Err's value.
          result = err.transfErr<T>();
        } catch (error, stackTrace) {
          result = Err<T>(error, stackTrace: stackTrace);
        }
      }
    }
    // Run `onFinalize` separately so its throws are absorbed into `result`
    // instead of escaping the async block and leaving `Async.value` to
    // reject with an uncaught error. Following standard `try/finally`
    // semantics, a thrown finalize error overrides whatever `result` held.
    if (onFinalize != null) {
      try {
        onFinalize();
      } on Err catch (err) {
        result = err.transfErr<T>();
      } catch (error, stackTrace) {
        result = Err<T>(error, stackTrace: stackTrace);
      }
    }
    return result;
  }());
}