It is not clear to me whether the the single subscriber streams in Dart actually save data they receive. If they do, is there a way to disable this, since this seems like a major memory leak?
With the new async* functions in Dart, do the streams produced by these store data?
The code-doc on the StreamController constructor says
The controller will buffer all incoming events until the subscriber is
registered.
To avoid queued events you can use a broadcast stream
new StreamController.broadcast(...);
or pause the subscription
StreamSubscription sub;
sub = s.listen((e) {
sub.pause();
// process event
sub.resume();
});
A stream created by async* behaves the same
import 'dart:async';
Stream<int> a() async* {
for (int i = 1; i <= 10; ++i) {
print('yield $i');
yield i;
}
}
main() {
a().listen((e) async {
await new Future.delayed(const Duration(seconds: 1));
print(e);
});
StreamSubscription sub;
sub = a().listen((e) async {
sub.pause();
await new Future.delayed(const Duration(seconds: 1));
print(e);
sub.resume();
});
}
try at DartPad
The first example prints
yield 1
yield 2
yield 3
yield 4
yield 5
yield 6
yield 7
yield 8
yield 9
yield 10
1
2
3
4
5
6
7
8
9
10
the second example (with pause) prints
yield 1
yield 2
yield 3
1
2
yield 4
3
yield 5
4
yield 6
5
yield 7
6
yield 8
7
yield 9
8
yield 10
9
10
I assume you mean a Future when referring to a single subscriber stream.
As far as I understand the new keywords don't change the actual behaviour. The async/async* will respectively create a Future/Stream which will buffer the data naturally until they are actually delivered. For example you can do both:
import 'dart:async';
Future single() async {
return 42;
}
main() async {
int r = await single();
print('ok: $r');
single().then((int val) {
print('val $val');
});
}
So in the end I'd say that they behave the same, you just express what you are doing in a different way. Thus the Stream will take data only if there are listeners. And then the data will be delivered as usual.
A quote from their website: One can implement streams manually using Stream and allied classes. Asynchronous generator functions are sugar for implementing such streams
Related
My rust code is like below.
#[tokio::main]
pub async fn main() {
for i in 1..10 {
tokio::spawn(async move {
println!("{}", i);
});
}
}
When run the code, I expect it to print 1 to 10 in a random sequence.
But it just print some random numbers:
1
3
2
Terminal will be reused by tasks, press any key to close it.
Why this is happenning?
https://docs.rs/tokio/latest/tokio/fn.spawn.html warns that:
There is no guarantee that a spawned task will execute to completion. When a runtime is shutdown, all outstanding tasks are dropped, regardless of the lifecycle of that task.
One solution that should work is to store all of the JoinHandles and then await all of them:
let mut join_handles = Vec::with_capacity(10);
for i in 1..10 {
join_handles.push(tokio::spawn(async move {
println!("{}", i);
}));
}
for join_handle in join_handles {
join_handle.await.unwrap();
}
P.S. In 1..10, the end is exclusive, so the last number is 9. You might want 1..=10 instead. (see https://doc.rust-lang.org/reference/expressions/range-expr.html)
what changes should I do to the following code to get the following output:
Task 1 complete
Task 4 complete
Task 2 complete
Task 3 complete with task 2 data
I currently getting the outputs given below:
Task 1 complete
Task 2 complete
Task 3 complete with task 2 data
Task 4 complete
import 'dart:async';
void main() {
performTasks();
}
void performTasks() async {
task1();
String task2Result = await task2();
task3(task2Result);
task4();
}
void task1() {
String result = 'task 1 data';
print('Task 1 complete');
}
Future<String> task2() async {
Duration threeSeconds = Duration(seconds: 3);
String result;
await Future.delayed(threeSeconds, () {
result = 'task 2 data';
print('Task 2 complete');
});
return result;
}
void task3(String task2Data) {
String result = 'task 3 data';
print('Task 3 complete with $task2Data');
}
void task4() {
String result = 'task 4 data';
print('Task 4 complete');
}
Don't call task4 after waiting for task2.
So:
void performTasks() async {
task1();
task4();
String task2Result = await task2();
task3(task2Result);
}
That looks pretty obvious, so I'm assuming your real problem is more complicated, and you can't move test4() around like that.
In that case, you should not use await. The await ensures that everything written after the await also executes after the awaited future has completed.
Instead you can fall back on the Future API:
void performTasks() { // no `async`
task1();
task2().then(task3); // no `await`
task4();
}
This sets up task2() to run, and when that is done, it calls task3 with the result. It doesn't wait for anything, though, and it executes task4 immediately after setting this up.
The then method on futures takes a callback, and eventually calls that with the result of the future. Here task3 takes one argument, so it can directly be used as that callback.
That assumes that task2's result is directly usable as an argument to task3. If not, and you have to capture the result and manipulate it first, you'd do it as:
void performTasks() { // no `async`
task1();
task2().then((result2) { // still no await here!
var argument3 = manipulate(result2);
// ... and whatever else you want to do
// between task2 completing and task3 starting.
task3(argument3);
});
task4();
}
in performTasks(), you should move task4(); right after task1();.
I have a collection, and now I need to iterate through the data in it, and then modify one of the fields, but this modification process is time consuming and requires asynchronous, I do not know how to write this code correctly.
Here are my fake code:
Stream<int> asynchronousNaturalsTo(int n) async* {
yield calculate(n).asStream();
}
Future<int> calculate(int i) async {
// async calculate result. maybe there is some long-running operate.
return Future.value(i + 10);
}
main() async {
// 1. I have a List that contains data needs be calculated by async.
for (int i = 0; i < 3; i++) {
// 2. calculate
var list = await asynchronousNaturalsTo(i);
// 3. finally, use the final result.
print(list);
}
}
How do I write the code?
thx...
I'd like to yield the CPU in a long-ish running computation from time to time, so that other activities can be performed in the same isolate. "await null" does what I want, but I wonder if there's a different, generally accepted Dart idiom to do the same thing?
Here's a bit of code, to illustrate what I mean:
import 'dart:async';
import 'dart:math';
void main(args) async {
burnCpu(1);
burnCpu(2);
burnCpu(3);
burnCpu(4);
}
int i = 0;
var r = new Random();
void burnCpu(int id) async {
while (i < 1000000) {
i++;
print("$id: $i");
for (int j = 0; j < 1000000; j++) {
var a = (j / r.nextDouble()).toString() + r.nextDouble().toString();
}
await null;
// See Dart language spec v.2.2 section 16.34, "Await Expressions"
}
}
As expected, this produces the following:
1: 1
2: 2
3: 3
4: 4
1: 5
2: 6
3: 7
4: 8
1: 9
2: 10
...
I'm pretty happy with just using "await null;", and if my understanding of the language spec is correct, it's guaranteed to work. But "await null", while logical, is a little obscure-looking. I searched around a bit to see if I could find a common idiom for this, but came up empty. Is there one?
I am having problems with futures in a Flutter app.
void saveCats() async {
var cats = await convertToCats(_rawData);
await DatabaseProvider.db.addCats(cats);
}
Future<List<Cat>> convertToCats(CatList catData) async {
var cats = <Cat>[];
await catData.forEach(key, value) async {
var pos = await calculatePos();
print('This should come first');
cats.add(Cat(name: key, pos: pos);
}
}
Future<int> calculatePos() async {
return await DatabaseProvider.db.calculatePos();
}
database.dart:
Future<void> addCats(List<Cat> cats) async {
print('This should come second');
// Do adding stuff here
}
Future<int> calculatePos() async {
// Some code to calculate the position
return pos;
}
In the above code, the saveCats function is called when a button is tapped. This function converts some raw data to a list of Cat models, and adds them to the database. As part of this conversion process, it calculates the pos of the cat in a list. My problem is that I would expect that, of my two print statements, the one in the forEach loop should come before the one in the addCats database function. But instead they appear in reverse order. Where am I going wrong?
You can't async/await in List.forEach() or Map.forEach() as both of them return void.
Either use
await Future.forEach([1, 2, 3], (num) async {
await asyncMethod(num);
});
or something similar;
https://api.flutter.dev/flutter/dart-async/Future/forEach.html
forEach often doesn't do what you expect, because the provided function runs as a closure.
It's more natural when you want to iterate over a list doing something to each element to use for (or one of the more functional type methods like map).
It's not clear what type CatList is, so this is approximate, but you'll want something more like:
Future<List<Cat>> convertToCats(CatList catData) async {
var cats = <Cat>[];
for (var i = 0; i < catData.length; i++) {
var key = catData[i].key;
var pos = await calculatePos();
print('This should come first');
cats.add(Cat(name: key, pos: pos));
}
return cats;
}
or
Future<List<Cat>> convertToCats(CatList catData) async {
return catData.map(...some mapping function...).toList();
}