doWhile static method
Performs an operation repeatedly until it returns false.
The operation, action, may be either synchronous or asynchronous.
The operation is called repeatedly as long as it returns either the bool
value true or a Future<bool> which completes with the value true.
If a call to action returns false or a Future that completes to
false, iteration ends and the future returned by doWhile is completed
with a null value.
If a call to action throws or a future returned by action completes
with an error, iteration ends and the future returned by doWhile
completes with the same error.
Calls to action may happen at any time,
including immediately after calling doWhile.
The only restriction is a new call to action won't happen before
the previous call has returned, and if it returned a Future<bool>, not
until that future has completed.
Example:
void main() async {
var value = 0;
await Future.doWhile(() async {
value++;
await Future.delayed(const Duration(seconds: 1));
if (value == 3) {
print('Finished with $value');
return false;
}
return true;
});
}
// Outputs: 'Finished with 3'
Implementation
static Future<void> doWhile(FutureOr<bool> action()) {
_Future<void> doneSignal = _Future<void>();
late void Function(bool) nextIteration;
// Bind this callback explicitly so that each iteration isn't bound in the
// context of all the previous iterations' callbacks.
// This avoids, e.g., deeply nested stack traces from the stack trace
// package.
nextIteration = Zone.current.bindUnaryCallbackGuarded((bool keepGoing) {
while (keepGoing) {
FutureOr<bool> result;
try {
result = action();
} catch (error, stackTrace) {
// Cannot use `_completeWithErrorCallback` because it completes
// the future synchronously.
doneSignal._asyncCompleteErrorObject(
_interceptCaughtError(error, stackTrace),
);
return;
}
if (result is Future<bool>) {
result.then(nextIteration, onError: doneSignal._completeError);
return;
}
keepGoing = result;
}
doneSignal._complete(null);
});
nextIteration(true);
return doneSignal;
}