cond method
Returns the value of the Pod when the test returns true.
The internal listener is held strongly via pinListener so dropping
the returned Resolvable without awaiting it cannot cause the
listener to be garbage collected before test succeeds.
Failure modes — all surface as Err on the returned Resolvable
rather than crashing the caller or hanging forever:
- Pod disposed before
testever matches →Err(StateError(...)). testthrows (sync first call or any later notify) →Err(thrown error).
Implementation
Resolvable<T> cond(bool Function(T value) test) {
// Bail out on a disposed pod: addStrongRefListener is a release-mode
// no-op on a disposed notifier, so subscribing would mean the
// SafeCompleter never resolves and the caller's await hangs.
if (isDisposed) {
return Sync.err(
Err(StateError('Cannot call cond() on a disposed pod')),
);
}
final finisher = SafeCompleter<T>();
late final VoidCallback check;
check = () {
// A throwing predicate must not leave the completer stuck — convert
// throws into an Err on the returned Resolvable.
try {
if (test(value)) {
finisher.complete(value).end();
_pendingConds?.remove(finisher);
unpinListener(check);
removeListener(check);
}
} catch (e, s) {
finisher.resolve(Sync.err(Err(e, stackTrace: s))).end();
_pendingConds?.remove(finisher);
unpinListener(check);
removeListener(check);
}
};
check();
if (finisher.isCompleted) {
return finisher.resolvable();
}
// A side-effecting predicate may have disposed the pod during the
// sync check above. Without this guard, subscribing would be a no-op
// and the finisher would never resolve.
if (isDisposed) {
finisher
.resolve(
Sync.err(
Err(
StateError(
'Pod was disposed during cond() predicate execution',
),
),
),
)
.end();
return finisher.resolvable();
}
(_pendingConds ??= <SafeCompleter<T>>{}).add(finisher);
pinListener(check);
addStrongRefListener(strongRefListener: check);
return finisher.resolvable();
}