waitFor<T> function
- @Deprecated("This functionality is incomplete and may be removed in a later version")
Suspends the stack, runs microtasks, and handles incoming events until
future
completes.
Deprecation notice
The waitFor
feature is deprecated.
The feature was intended to solve a particular problem for existing code,
a problem introduced by a breaking change to the platform libraries.
The waitFor
function is not suitable for general use.
The feature has shortcomings that can affect other code
running in the same isolate, including:
- A function call that looks synchronous may cause other asynchronous events to run before it returns. This is something synchronous code can usually assume not to happen, and some code may have been written to take advantage of that assumed behavior. Such code can fail in unexpected ways.
- Multiple nested calls to
waitFor
may block each other since the most recent call always needs to complete before any other call can complete. Judicious use ofwaitFor
is necessary to avoid unexpected deadlocks which wouldn't happen if usingawait
instead. If more than one library in the same program is usingwaitFor
, then it's hard to avoid or control whether such blocking will happen.
The feature is not actively maintained. It will remain as-is to support the original problem it was added to solve, at least until that problem can be solved in some other way.
Call semantics
This call does the following:
- While
future
is not completed:- suspends the current execution stack,
- runs the microtask queue until it is empty,
- waits until the message queue is not empty,
- handles messages on the message queue, plus their associated microtasks, until the message queue is empty,
- resumes the original stack.
This function breaks the usual promise offered by Dart semantics that message handlers and microtasks run to completion before the next message handler or microtask begins to run. Of particular note is that use of this function in a finally block will allow microtasks and message handlers to run before all finally blocks for an exception have completed, possibly breaking invariants in your program.
Use of this function should be considered a last resort when it is not
possible to convert a Dart program entirely to an asynchronous style using
async
and await
.
If the Future completes normally, its result is returned. If the Future completes with an error, the error and stack trace are wrapped in an AsyncError and thrown. If a microtask or message handler run during this call results in an unhandled exception, that exception will be propagated as though the microtask or message handler was the only Dart invocation on the stack. That is, unhandled exceptions in a microtask or message handler will skip over stacks suspended in a call to waitFor.
If the optional timeout
parameter is passed, waitFor throws a
TimeoutException if the Future is not completed within the specified
period.
Calls to waitFor may be nested. Earlier invocations will not complete until subsequent ones do, but the completion of a subsequent invocation will cause the previous invocation to wake up and check its Future for completion.
Please be aware that nesting calls to waitFor can lead to deadlock if subsequent calls block waiting for a condition that is only satisfied when an earlier call returns.
Implementation
@Deprecated(
"This functionality is incomplete and may be removed in a later version")
T waitFor<T>(Future<T> future, {Duration? timeout}) {
late T result;
bool futureCompleted = false;
Object? error;
StackTrace? stacktrace;
future.then((T r) {
futureCompleted = true;
result = r;
}, onError: (e, st) {
error = e;
stacktrace = st;
});
late Stopwatch s;
if (timeout != null) {
s = new Stopwatch()..start();
}
Timer.run(() {}); // Ensure there is at least one message.
while (!futureCompleted && (error == null)) {
Duration? remaining;
if (timeout != null) {
if (s.elapsed >= timeout) {
throw new TimeoutException("waitFor() timed out", timeout);
}
remaining = timeout - s.elapsed;
}
_WaitForUtils.waitForEvent(timeout: remaining);
}
if (timeout != null) {
s.stop();
}
Timer.run(() {}); // Ensure that previous calls to waitFor are woken up.
if (error != null) {
throw new AsyncError(error!, stacktrace);
}
return result;
}