-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Warn on using generator as void function #42717
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
To stop an It's probably possible to create some I'd probably just use non-async code for this, keep the This code: _locationSubscription = location.onLocationChanged.listen(
(location) async* {
if (location.isNotNull) {
yield LocationState.sendData(location: updateLocation(location));
}
},
); does not do what you expect. Each call to the inner function creates a new stream. Nobody listens to that stream (the |
Ok, is it possible to get it working as I would have it expected? This is failing without any hint right now. Maybe Dart or the compiler/ analyzer can catch that? using |
We won't make the As for the "no warning", maybe the analyzer could issue a warning if you use a If you just want your code to keep working, I'd simply not switch to (You should use Or you could write up a cancelable stream abstraction, like: import "dart:async";
/// A stream which can be interrupted at any point.
///
/// Any subscription on [tream] can be cancelled at any
/// time using [interrupt].
abstract class InterruptibleStream<T> {
/// Creates an interruptible stream which forwards to [source]
///
/// Listening on the interruptible [stream] provides the same
/// events as listening on [source] until either [source]
/// is done or the [stream] is [interrupt]ed.
factory InterruptibleStream(Stream<T> source) =>
_InterruptibleStream<T>(() => source);
/// Creates an interruptible stream which forwards to `source()`
///
/// Listening on the interruptible [stream] creates a new
/// stream by calling [source], then provides the same
/// events as listening on that stream until either it
/// is done or the [stream] is [interrupt]ed.
factory InterruptibleStream.multi(Stream<T> source()) =>
_InterruptibleStream<T>(source);
/// The interruptible stream itself.
///
/// All subscriptions on this stream can be interrupted using
/// [interrupt].
Stream<T> get stream;
/// Interrupt all current subscriptions on [stream].
///
/// An interrupted subscription cancels its underlying
/// subscription, then emits a done event.
///
/// If [error] is supplied, it is emitted as an error
/// event after the cancel has completed, before
/// emitting a done event.
void interrupt([Object error, StackTrace stack]);
}
class _InterruptibleStream<T> implements InterruptibleStream<T> {
final Stream<T> stream;
/// Set of currently active subscriptions that [interrupt] should end.
final Set<_InterruptibleSubscription<T>> _subscriptions;
_InterruptibleStream(Stream<T> source()) : this._(source, {});
_InterruptibleStream._(Stream<T> source(), this._subscriptions)
: stream = Stream<T>.multi((StreamController<T> c) {
_subscriptions.add(_InterruptibleSubscription<T>(
_subscriptions, c, source().listen(null)));
});
void interrupt([Object? error, StackTrace? stack]) {
for (var subscription in _subscriptions.toList()) {
if (_subscriptions.remove(subscription)) {
subscription.interrupt(error, stack);
}
}
}
}
class _InterruptibleSubscription<T> {
final StreamController<T> controller;
final StreamSubscription<T> subscription;
final Set<_InterruptibleSubscription<T>> owner;
_InterruptibleSubscription(this.owner, this.controller, this.subscription) {
controller.onPause = subscription.pause;
controller.onResume = subscription.resume;
controller.onCancel = _onCancel;
subscription.onData(controller.add);
subscription.onError(controller.addError);
subscription.onDone(_onDone);
}
FutureOr<void> _onCancel() {
owner.remove(this); // Can no longer be interrupted.
return subscription.cancel();
}
void _onDone() {
owner.remove(this); // Can no longer be interrupted.
controller.close();
}
void interrupt(Object? error, StackTrace? stack) {
var cancelResult = subscription.cancel();
void end([_]) {
if (error != null) controller.addError(error, stack);
controller.close();
}
if (cancelResult is Future<void>) {
cancelResult.then<void>(end, onError: (e, s) {
controller.addError(e, s);
end();
});
} else {
scheduleMicrotask(end);
}
}
} (The code as written requires a dev-release and null safety experiment for now). |
I was expecting this (cancelling a subscription from a stream using await-for in an async generator) to just work. Is this something that will be fixed in future versions of Dart? |
Not sure what "just work" means here. Cancelling the subscription of a stream being generated by an |
Simply use await for (final event in stream) {
if (event.length==0) {
yield Object();
// Breaks the await for
break;
}
//...
} |
if a break is used, it looks like something prevents the enclosing task from exiting. think this might be a bug. however, if using a StreamIterator, instead of "await for", the "break" works as expected, and i don't get into trouble w/ the outer enclosing task exiting as a general run, i "think" "await for" must always be allowed to run to completion, otherwise bad things happen. |
There might be a bug somewhere, but that's impossible to say without seeing the actual code which fails. Breaking an |
I also ran into this problem (in a slightly more specific domain) and figured I'd share my work-around. I have a function which re-maps a never-ending stream (and cannot use
As long as it is not a broadcast stream, you can check to see if a controller has any subscribers, and if it doesn't you can break. For example:
This wouldn't work for broadcast streams, since you could potentially cancel all listeners and then add a new one later. It also doesn't close immediately, but at least exists cleanly eventually (assuming another event is raised). This has the advantage (for my specific use case) that the client doesn't need to explicitly set a flag to clean up. |
Since the stream returned from an |
Hello, When using import 'dart:async';
import 'package:rxdart/rxdart.dart';
void main() async {
late StreamSubscription<void> sub;
sub = getStream().listen((v) {
print('Cancelling...');
sub.cancel().then((_) => print('Cancelled...'));
});
await Future<void>.delayed(const Duration(seconds: 1));
print('---');
getStream().take(1).forEach(print);
}
Stream<String> getStream() async* {
final s = Stream.value("Hello").concatWith([Rx.never()]);
// or: final s = BehaviorSubject.seeded('Hello');
try {
await for (final innerValue in s) {
yield innerValue;
}
} finally {
print('Done');
}
} |
@hoc081098 this sounds like an unrelated problem, not fitting this issue which is now an analyzer enhancement request. Do file a separate new issue instead. (Also, do try to reproduce without using Rx streams, otherwise the bug may be in those, and it should be filled in the Rx-stream repo if it is. Although I'm guessing the bug is a known issue with the current |
I can reproduce it without import 'dart:async';
void main() async {
late StreamSubscription<void> sub;
sub = getStream().listen((v) {
print('Cancelling...');
sub.cancel().then((_) => print('Cancelled...'));
});
await Future<void>.delayed(const Duration(seconds: 1));
print('---');
getStream().take(1).forEach(print);
}
Stream<String> getStream() async* {
final controller = StreamController<String>();
controller.onListen = () {
controller.add('Hello');
};
try {
await for (final innerValue in controller.stream) {
yield innerValue;
}
} finally {
print('Done');
}
} Console:
|
Cool. Do file as a new issue, it still isn't relevant to this issue. 😉 |
I am using a stream to read out location data in a bloc. I have a start and a stop event. In the stop method, I cancel the stream subscription. When I use
listen
to a stream toyield
the state the inside where theyield
statement is never gets called.What really bugs me that I don't get any error message warning or compiler error
When I replace the
listen
toawait for
I don't see any wayto stop this from yielding events because the subscription handle is gone.
Any ideas? Any explanations?
This tracker is for issues related to:
Dart core libraries ("dart:async", "dart:io", etc.)
Dart SDK Version 2.8.4
Windows, MacOSX, and Linux
The text was updated successfully, but these errors were encountered: