try/catch block in calling generator function is never called - dart

Can someone please explain to me why the calling generator function try/catch block is never run?
/// Dart program with nested stream for showing why try/catch blocs
/// in calling generator function is never called.
void main() async {
try {
await for (var val in generator()) {
print('Value: $val');
}
} catch (e) {
print(e.toString());
}
}
Stream<int> generator() async* {
for (int i = 0; i < 10; i++) {
try {
yield* generator2();
yield i;
} catch (e) {
// This catch block is never called. Never!
print('Woot');
}
}
}
Stream<int> generator2() async* {
for (int i = 100; i < 110; i++) {
if (i == 105) {
throw GeneratorException('Error on generating $i');
}
yield i;
}
}
class GeneratorException {
const GeneratorException(this.message);
final String message;
}
You can also try the below dartpad:
https://nullsafety.dartpad.dev/437b31a747cf7bacfa2332408c98f849

I think it is by design if you read the Dart specification:
If m is marked async*(9), then:
...
The o stream is listened to, creating a subscription s, and for each event x, or error e with stack trace t, of s:
...
Otherwise, x, or e with t, are added to the stream associated with m in the order they appear in o. Note that a dynamic error occurs if x is added and the dynamic type of x is not a subtype of the element type of said stream. The function m may suspend.
https://dart.dev/guides/language/specifications/DartLangSpec-v2.10.pdf
So when you do yield* you are forwarding all events from this Stream including errors.

Related

How to return a stream based on another stream's event

I want to return a stream based on another stream's value/event.
For example, if I have a 2 streams, stream1 and stream2, I want to create a function which returns as stream either stream2 or null, depending on the value of stream1. How can I do that?
I tried to map stream1 and based on the event to yield stream2, but it does not work. I also can't listen to stream1 and based on the event yield stream2.
Stream<Data1?> getStream1() async* {
// yield stream1
}
Stream<Data2?> getStream2(dynamic value) async* {
//yield stream2 based on value
}
Stream<Data2?> getStream12() async* {
final stream1 = getStream1();
// not working
yield* stream1.map((event) => event == null ? null : getStream2(event.var));
// I also tried await for, but it has a strange behaviour
// if I listen to the stream after (it's inconsistent)
await for (var event in stream1) {
if (event == null) {
yield null;
} else {
yield* getStream2(event.var);
}
}
}
Is there any solution to this, preferable without any additional package dependencies like rxdart, just pure dart?
Looks like await for have to work...
Can you try this?
Stream<int> getStream1() async* {
yield 1;
await Future.delayed(Duration(seconds: 1));
yield null;
await Future.delayed(Duration(seconds: 1));
yield 2;
await Future.delayed(Duration(seconds: 1));
}
Stream<int> getStream2(dynamic value) async* {
yield value;
await Future.delayed(Duration(seconds: 1));
yield value;
await Future.delayed(Duration(seconds: 1));
}
Stream<int> getStream12() {
return getStream1().asyncExpand(
(event) => event == null ? Stream.value(null) : getStream2(event),
);
}
void main() {
getStream12().listen(print);
}
Output:
1
1
null
2
2

How to return a Stream in Dart without looping through it

I have a Stream in my code, which I've done some modifications / transformations to. I'd like to return that Stream as the result of my function.
This code works:
Stream<int> returnStream2() async* {
var list = <int>[1,2,3];
var result = Stream<int>.fromIterable(list);
await for( var i in result ) {
yield i;
}
}
Why doesn't this simpler and more readable version work?
Stream<int> returnStream1() async* {
var list = <int>[1,2,3];
var result = Stream<int>.fromIterable(list);
return result;
}
I get an error message "Can't return a value from a generator function (using the 'async*' modifier)
Either use yield* as you already discovered, or just don't make the function an async* function. If this stream is the only thing you yield, you can just do:
Stream<int> returnStream1() {
var list = <int>[1,2,3];
var result = Stream<int>.fromIterable(list);
return result;
}
If you do other things, then yield* stream; or await for (var event in stream) yield event; are both options (they differ in how they handle error events from the stream, but for plain data events the two should be equivalent).
I just discovered the yield* keyword, which solves my problem:
Stream<int> returnStream1() async* {
var list = <int>[1,2,3];
var result = Stream<int>.fromIterable(list);
yield* result;
}

How do these function return values work in Dart?

the official Bloc documentation gives this code snippet:
Stream<int> countStream(int max) async* {
for (int i = 0; i < max; i++) {
yield i;
}
}
This looks very simple to me, until I realize that the function does not return anything (no returnstatement), but yields all the time (which is to me - coming from python - not the same as a return statement).
There's also this example, which is even more clear:
Future<int> sumStream(Stream<int> stream) async {
int sum = 0;
await for (int value in stream) {
sum += value;
}
return sum;
}
Here there's an int returned explicitly, but if I read this correctly the function definition should return a Future<int>type:
Future<int> sumStream(Stream<int> stream) async { //...
Now, how does this work? I'm a bit confused here.
Thanks in advance!
Axel.
For your first example, countStream is a Stream of integer designed with an async*. (take care of the star)
In this stream definition, Yield emit a new integer on the stream flow (of integer as said by stream < int > .
If you consume the stream, you will get 0,1,2,3,4,...,max-1
In the second snippet, read it as : sumStream is a Future that must return an int.
More generally, A Future < T > instance produces a value of type T.
sumStream will consume a stream (given in parameter) and as long as this stream has values, sum them and return the last sum when finished. Sum is an integer : it's ok with Future definition.
Using both, you can have :
> void main() async {
print(await sumStream(countStream(5)));
}
result : 10 (0+1+2+3+4)
As sumStream is a Future, main can wait for asynchronous execution: it declares its intention to use async function (async declaration), then it can use await to stop its own execution until sumStream returns.
Another way if you leave main without async is to add a .then clause to the Future:
void main() {
sumStream(countStream(5)).then((x)=>print(x));
print("after sumStream");
}
Result :
after sumStream
10
As you can see main pass over sumStream it has started but which is not finished and print("after sumStream") first.
Then the main finished, but Dart knows there is still async job running and it will wait until all is done, so we can see the result, but once Future sumStream is finished.
HTH
This looks very simple to me, until I realize that the function does not return anything (no return statement)
First of all, this function returns Stream<int> result value as specified in function declartion. This function often called as the generator function.
Stream<int> countStream(int max) async*
The function body code (generator) will generate the int values and yield these generated values to the initially returned Stream<int> stream.
The statement yield means in this case the following logic of work:
Add a value to Stream<int> controller and return to the next statement in the body of the function that follows that statement yield.
Something like this:
import 'dart:async';
Future<void> main() async {
await for (final i in countStream(5)) {
print(i);
}
}
Stream<int> countStream(int max) {
int i;
final _ctl$ = StreamController<int>();
void Function(int) _switchState$;
_switchState$ = (int _state$) {
while (true) {
switch (_state$) {
case 0:
// for (int i = 0;
i = 0;
_state$ = 1;
break;
case 1:
// i < max;
if (i < max) {
_state$ = 2;
} else {
return;
}
break;
case 2:
// yield i;
_ctl$.add(i);
Timer.run(() => _switchState$(3));
return;
case 3:
// i++
i++;
_state$ = 1;
break;
}
}
};
Timer.run(() => _switchState$(0));
return _ctl$.stream;
}
Result:
0
1
2
3
4

What's the difference between async and async* in Dart?

I am making an application using flutter framework .
During this I came across with the keywords in Dart async and async*.
Can anybody tell me what's the difference between them?
Short answer
async gives you a Future
async* gives you a Stream.
async
You add the async keyword to a function that does some work that might take a long time. It returns the result wrapped in a Future.
Future<int> doSomeLongTask() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}
You can get that result by awaiting the Future:
main() async {
int result = await doSomeLongTask();
print(result); // prints '42' after waiting 1 second
}
async*
You add the async* keyword to make a function that returns a bunch of future values one at a time. The results are wrapped in a Stream.
Stream<int> countForOneMinute() async* {
for (int i = 1; i <= 60; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
The technical term for this is asynchronous generator function. You use yield to return a value instead of return because you aren't leaving the function.
You can use await for to wait for each value emitted by the Stream.
main() async {
await for (int i in countForOneMinute()) {
print(i); // prints 1 to 60, one integer per second
}
}
Going on
Watch these videos to learn more, especially the one on Generators:
Isolates and Event Loops
Futures
Streams
async / await
Generators
Marking a function as async or async* allows it to use the async/await for a Future.
The difference between both is that async* will always return a Stream and offer some syntactical sugar to emit a value through the yield keyword.
We can therefore do the following:
Stream<int> foo() async* {
for (int i = 0; i < 42; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
This function emits a value every second, which increments every time.
Solution, Origins and Insights
This answer includes simplified and easy to understand examples
async
The async computation cannot provide a result immediately when it is started because the program may need to wait for an external response like:
Reading a file
Querying a database
Fetching data from an API
Instead of blocking all computation until the result is available, the asynchronous computation immediately returns a Future object which will eventually "complete" with the result.
Example (This type of async call can be used only without returning a response):
void main() async {
// The next line awaits 5 seconds
await Future.delayed(Duration(seconds: 5));
// Pseudo API call that takes some time
await fetchStocks();
}
Future
A Future represents a computation that doesn’t complete immediately. Whereas a normal function returns the result, an asynchronous function returns a Future, which will
eventually contain the result. The Future will tell you when the result is ready.
Future is appended when the async function returns a value
Represents the result of a single computation (in contrast to a Stream)
Example:
Future<String> fetchUserOrder() =>
// Imagine that this function is more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
void main(List<String> arguments) async {
var order = await fetchUserOrder();
// App awaits 2 seconds
print('Your $order is ready');
}
Stream
A source of asynchronous data events.
A Stream provides a way to receive a sequence of events. Each event is either a data event, also called an element of the stream.
Stream is a sequence of results
From stream you get notified for results (stream elements)
async* (streams)
async* is an asynchronous generator that returns a Stream object. Made to create streams.
An example of using a stream and async*:
// Creating a new stream with async*
// Each iteration, this stream yields a number
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}
void main(List<String> arguments) {
// Calling the stream generation
var stream = createNumberStream(5);
// Listening to Stream yielding each number
stream.listen((s) => print(s));
}
Result:
1
2
3
4
5
Bonus: Transforming an Existing Stream
If you already have a stream, you can transform it to a new stream based on the original stream’s events.
Example (same code as before but with a twist):
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}
// This part is taking a previous stream through itself and outputs updated values
// This code multiplies each number from the stream
Stream<int> createNumberDoubling(Stream<int> chunk) async* {
await for (final number in chunk) {
yield number*2;
}
}
void main(List<String> arguments) {
// Here we are Transforming the first stream through createNumberDoubling stream generator
var stream = createNumberDoubling(createNumberStream(5));
stream.listen((s) => print(s));
}
Result:
2
4
6
8
10
Solution
The async and async* are close relatives, they are even from the same library dart:async
The async represent a Future and a one-time exchange while the async* represents a Stream, a stream of multiple events
Async functions execute synchronously until they reach the await keyword. Therefore, all synchronous code within an async function body executes immediately.
Future<int> foo() async {
await Future.delayed(Duration(seconds: 1));
return 0;
}
Async* is used to create a function that returns a bunch of future values one at a time. Each result is wrapped in a Stream.
Stream<int> foo() async* {
for (var i = 0; i < 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
async* will always return a Stream
Stream<int> mainStream(int value) async* {
for (int i = 1; i <= value; i++) {
yield i;
}
}
async returns the result wrapped in the Future. So it might take longer time. See the below example:
void main() async {
// The next line awaits 10 seconds
await Future.delayed(Duration(seconds: 10));
}

How to implement Iterable<E> in dart?

I still havn't understood how to deal with Iterable/Iterator in Dart.
I think I have to give up and simply return Lists but that's not what I want since it will
lead bad performance in my case.
What I want is to understand how to implement my own Iterable/Iterator.
Why do both of these attempts fail?
library foo;
import 'dart:collection';
// Both attemps below raises the following error:
// ==============================================
//
// Closure call with mismatched arguments: function 'moveNext'
//
// NoSuchMethodError: incorrect number of arguments passed to method named 'moveNext'
// Receiver: Closure: (dynamic) => Iterator<int> from Function 'iterator':.
// Tried calling: moveNext()
main() {
Iterable<int> iterable1 = new OddsIterableDartStyle([1,2,4,6,7,8,9]);
for (int i in iterable1)
print("ODD: $i");
Iterable<int> iterable2 = new OddsIterableJavaStyle([1,2,4,6,7,8,9]);
for (int i in iterable2)
print("ODD: $i");
}
// ------------------------------------------
class OddsIterableDartStyle extends Object with IterableMixin<int> {
List<int> _ints;
OddsIterableDartStyle(this._ints);
Iterator<int> iterator() {
return new OddsIterator(this);
}
}
// ------------------------------------------
class OddsIterableJavaStyle implements Iterable<int> {
List<int> _ints;
OddsIterableJavaStyle(this._ints);
Iterator<int> iterator() {
return new OddsIterator(this);
}
}
// ------------------------------------------
class OddsIterator implements Iterator<int> { // Iterate over odd numbers
List<int> _ints;
int _index;
OddsIterator(this._ints) {
_index = -1;
}
bool moveNext() {
while (++_index < _ints.length) {
if (_ints[_index].isOdd)
return true;
}
return false;
}
int get current => (_index < 0) ? null : _ints[_index];
}
I see two immediate problems:
iterator is a getter. The code shouldn't read Iterator<int> iterator() { ... }, it should be Iterator<int> get iterator { ... } instead.
Your iterators are expecting the underlying integer lists, but you are passing in the wrapper. You probably want to construct your iterator like new OddsIterator(_ints), not like new OddsIterator(this).
Btw, Iterator is supposed to return null if you call current and you have already moved beyond the end.
class Count extends Iterable with Iterator {
Count([this.limit = 10]);
int limit;
int i = 0;
#override
int get current => i;
#override
bool moveNext() {
i++;
return i <= limit;
}
#override
Iterator get iterator => this;
}

Resources