guard<T> method
Null safety
- Future<
T> body(
Calls the given callback in a new async scope. The callback argument is the asynchronous body of the calling method. The calling method is said to be "guarded". Nested calls to guarded methods from within the body of this one are fine, but calls to other guarded methods from outside the body of this one before this one has finished will throw an exception.
This method first calls guardSync.
Implementation
static Future<T> guard<T>(Future<T> Function() body) {
guardSync();
final Zone zone = Zone.current.fork(
zoneValues: <dynamic, dynamic>{
_scopeStack: true, // so we can recognize this as our own zone
}
);
final _AsyncScope scope = _AsyncScope(StackTrace.current, zone);
_scopeStack.add(scope);
final Future<T> result = scope.zone.run<Future<T>>(body);
late T resultValue; // This is set when the body of work completes with a result value.
Future<T> completionHandler(dynamic error, StackTrace? stack) {
assert(_scopeStack.isNotEmpty);
assert(_scopeStack.contains(scope));
bool leaked = false;
_AsyncScope closedScope;
final List<DiagnosticsNode> information = <DiagnosticsNode>[];
while (_scopeStack.isNotEmpty) {
closedScope = _scopeStack.removeLast();
if (closedScope == scope) {
break;
}
if (!leaked) {
information.add(ErrorSummary('Asynchronous call to guarded function leaked.'));
information.add(ErrorHint('You must use "await" with all Future-returning test APIs.'));
leaked = true;
}
final _StackEntry? originalGuarder = _findResponsibleMethod(closedScope.creationStack, 'guard', information);
if (originalGuarder != null) {
information.add(ErrorDescription(
'The test API method "${originalGuarder.methodName}" '
'from class ${originalGuarder.className} '
'was called from ${originalGuarder.callerFile} '
'on line ${originalGuarder.callerLine}, '
'but never completed before its parent scope closed.'
));
}
}
if (leaked) {
if (error != null) {
information.add(DiagnosticsProperty<dynamic>(
'An uncaught exception may have caused the guarded function leak. The exception was',
error,
style: DiagnosticsTreeStyle.errorProperty,
));
information.add(DiagnosticsStackTrace('The stack trace associated with this exception was', stack));
}
throw FlutterError.fromParts(information);
}
if (error != null) {
return Future<T>.error(error! as Object, stack);
}
return Future<T>.value(resultValue);
}
return result.then<T>(
(T value) {
resultValue = value;
return completionHandler(null, null);
},
onError: completionHandler,
);
}