Sync<T extends Object> constructor
Sync<T extends Object> (
- @mustBeAnonymous @noFutures T noFutures(), {
- @noFutures TOnErrorCallback<
T> ? onError, - @noFutures TVoidCallback? onFinalize,
Creates a Sync executing a synchronous function noFutures.
IMPORTANT:
Do not use any Futures in noFutures to ensure errors are be
caught and propagated.
Implementation
factory Sync(
@mustBeAnonymous @noFutures T Function() noFutures, {
@noFutures TOnErrorCallback<T>? onError,
@noFutures TVoidCallback? onFinalize,
}) {
assert(
isSubtype<T, Never>() || !isSubtype<T, Future<Object>>(),
'$T must never be a Future.',
);
Result<T> result;
try {
result = Ok(noFutures());
} 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`.
// Following standard `try/finally` semantics, a thrown finalize error
// overrides whatever `result` held — a failed cleanup is a meaningful
// failure mode that callers need to see.
if (onFinalize != null) {
try {
onFinalize();
} on Err catch (err) {
result = err.transfErr<T>();
} catch (error, stackTrace) {
result = Err<T>(error, stackTrace: stackTrace);
}
}
return Sync.result(result);
}