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));
}
Related
In Dart, when two or more tasks are waiting on the same Future, when the Future completes, do the tasks get notified/run in the order that they did the await i.e. the first to do an await is the first to run.
Is this code guaranteed to output 2
int res = 0;
Future<void> foo1 () async
{
await Future.delayed(Duration(seconds: 2));
res = 2;
}
void main() async
{
await foo1();
print(res);
}
and what about this code, slightly less obvious
int res = 0;
Future<void> foo1 () async
{
await Future.delayed(Duration(seconds: 2));
}
Future<void> foo2 (Future<void> f1) async
{
await f1;
res = 2;
}
Future<void> foo3 (Future<void> f1) async
{
await f1;
res = 3;
}
void main() async
{
res = 0;
Future<void> f1 = foo1();
foo3(f1);
foo2(f1);
await f1;
print(res);
}
There is no guarantee that the callbacks get called in the order they were added.
The dart:async library code tries to call the callbacks in that order, but it's best-effort only. There are cases, typically where one callback is added before the future is completed, and the other is added after, where it's possible to get called in a different order, because they are handled by different code paths.
It's not easy to trigger those cases, the timing has to be just right, but it is possible.
In the example here, there are three awaits on the f1 future. It's most likely that the printed value will be 2 (because nothing fancy is happening), but both 3 and 0 are allowed results.
Code printed 2.
int res = 0;
Future<void> foo1() async {
await Future.delayed(const Duration(seconds: 2));
print('foo1 method res 1: $res');
res = 2;
print('foo1 method res 2: $res');
}
void main() async {
await foo1();
print('last res: $res');
}
🧑💻 Code Test Output
foo1 method res 1: 0
foo1 method res 2: 2
last res: 2
My goal is to asynchronously return a lazy Iterable created by a generator function. I don't want to return a Stream or use the asnyc* generator since the generation can be done synchronously, only some previous code needs to run asynchronously.
Currently I'm doing it like this, but I wonder if there is any better way to do this?
If this is the only option I probably should outsource the generator function.
Future<Iterable<String>> someAsyncFunction() async {
// some async task
await Future.delayed(Duration(seconds: 1));
Iterable<String> x () sync* {
for (var s in ['1', 'b', 'c']) {
yield s;
}
}
return x();
}
print(await someAsyncFunction());
I was using a self invoked function before, but I wasn't able to specify a return type on it so I changed it to the code above.
Future<Iterable<String>> someAsyncFunction() async {
// some async task
await Future.delayed(Duration(seconds: 1));
return () sync* {
for (var s in ['1', 'b', 'c']) {
yield s;
}
}();
}
Your approach looks perfectly fine.
What I'd probably do myself is to have the function creating the literal as a helper function outside the async function:
Future<Iterable<String>> someAsyncFunction() async {
// some async task
await Future.delayed(Duration(seconds: 1));
return _iterateTheValues(the, values);
}
Iterable<String> _iterateTheValues(Some the, Other values) sync* {
for (var s in combine(the, values)) { // or use yield*.
yield s;
}
}
If you can create a useful iterable from some values after doing an asynchronous operation to find those values, it seems plausible that you'll eventually want to do the same thing with existing values. Maybe just for testing, maybe using a cache. It just feels like the synchronous iteration could be a thing of its own, separate from the asynchronous initialization needed for it.
I have a function that might take a few seconds to execute, and it is synchronous. Does:
String slowFunction() { ... }
...
Future<String>(() => slowFunction());
change it to asynchronous?
If I need its result in the next step, does this code make sense?
Future<void> anotherFunction() async {
// other async calls with Futures and await
...
final result = await Future<String>(() => slowFunction());
print(result);
...
// do something else with result
}
Seems kind of strange to create a Future only to immediately await on it. Should I just call the function? I guess it kind of 'yields' and allows other code to be executed before, but does such code have any use?
There is no point in making a process that is inherently synchronous and dressing it up as an asynchronous one. This is due to how asynchronicity (more generally referred to as "concurrency") works, not just in Dart but in general. Concurrency is just a programming trick to make multiple operations run interleaved with each other in the same thread, giving the illusion of true parallelism (which is where different threads or processes run simultaneously). This allows processes that would normally block while waiting for a resource to be put off until later as the program carries on with other things.
If you were to take a synchronous process that blocks due to work being actively done, either the program will block anyway as the "async" code executes just as it would otherwise do or the program will block just as long but at a later time. Either way, you are still blocking your program with a long-running process.
Take the following for example, which is what you ask: take a long-running process and wrap it in a Future, thus making it "asynchronous":
String slowFunction() { ... }
...
String result = await Future(slowFunction);
In normal concurrency, this will put slowFunction in the async queue. The next time the program has some downtime (in between UI draw calls, for example) it will pull that function out of the queue and process it. And thats when it will block for 2-3 seconds while the function executes.
In Dart, though, it works slightly differently. Because slowFunction is not an async function and doesn't await anything, Dart will attempt to run it synchronously anyway, in which case you needn't have bothered wrapping it in a Future in the first place.
You have two options here if you want to break up the operation of your synchronous function. Either you have to break it up into distinct operations that you can await between (which is itself a somewhat complicated process, isn't always possible, and is generally a good source of code smell), or you offload the function to a separate thread altogether, employing parallelism rather than mere concurrency.
Dart is single-threaded, but it can be multi-processed through the use of isolates. (An isolate is Dart's name for a child process and is as close to true multithreading that you can get in Dart.) By wrapping your function in an Isolate, you can run the work on an entirely separate process. That way, if that process blocks for 2-3 seconds, it won't affect the bulk of your app at all.
There is a catch, though. Because isolates are completely different processes, there is no sharing of memory whatsoever. That means any data that the isolate has access to has to be manually passed in through the use of "ports", namely SendPort and ReceivePort. This naturally makes isolate programming a bit of a pain, but in exchange, you won't run into things like your program having race conditions or getting deadlocked. (Because of shared memory problems, at least. Strictly speaking, there are plenty of other ways to get deadlocks and race conditions.)
Using an Isolate works like so:
// main process
void createIsolate() async {
ReceivePort isolateToMain = ReceivePort();
isolateToMain.listen((data) {
// Listen for data passed back to the main process
});
Isolate myIsolateInstance = await Isolate.spawn(myIsolate, isolateToMain.sendPort);
}
// isolate process
void myIsolate(SendPort mainToIsolate) {
final result = slowFunction();
mainToIsolate.send(result);
}
I have a function that might take a few seconds to execute, and it is synchronous. Does:
String slowFunction() { ... }
...
Future<String>(() => slowFunction());
change it to asynchronous?
Simply returning a Future will not make your function asynchronous in the way that you probably want.
When you call an asynchronous function, as much of it as possible will be executed synchronously. That is, the function will execute synchronously until it reaches the first await (i.e., until it returns a Future). Since a Dart isolate is single-threaded, if you want other work to be able to occur concurrently with your long-running operation, slowFunction would internally need to use await (which is syntactic sugar for creating Future.then() callbacks) to allow execution to yield.
Consider the following code:
Future<void> longRunningOperation1() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation1: $i');
}
}
}
Future<void> longRunningOperation2() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation2: $i');
}
}
}
Future<void> main() async {
await Future.wait([longRunningOperation1(), longRunningOperation2()]);
}
You will see that longRunningOperation1 and longRunningOperation2 never overlap; one always runs to completion before the other one starts. To allow the operations to overlap with minimal changes, you could do:
Future<void> longRunningOperation1() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation1: $i');
await Future.delayed(Duration.zero);
}
}
}
Future<void> longRunningOperation2() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation2: $i');
await Future.delayed(Duration.zero);
}
}
}
I am using a wrapper to spawn slow operations into a separate Isolate and return aFuture. It also allows to pass the function to run and some arguments as well.
import 'dart:async';
import 'dart:isolate';
/// Example
///
/// ```
/// main() async {
/// String str;
/// str = await runAsync<String, String Function(String)>(sing, ["lalalala"]);
/// print(str);
///
/// str = await runAsync<String, Function>(song);
/// print(str);
/// }
///
/// String sing(String str) => "Singing: " + str;
/// String song() => "lololololo";
/// ```
Future<R> runAsync<R, F>(F func, [List<dynamic> parameters]) async {
final receivePort = ReceivePort();
await Isolate.spawn(asyncRunner, receivePort.sendPort);
// The 'asyncRunner' isolate sends it's SendPort as the first message
final sendPort = await receivePort.first;
final responsePort = ReceivePort();
sendPort.send([responsePort.sendPort, func, parameters ?? []]);
final res = await responsePort.first;
if (res is! R)
return Future.error(res);
else if (res == null) return null;
return res as R;
}
// Isolate entry point
void asyncRunner(SendPort sendPort) async {
// Open the ReceivePort for incoming messages
final port = ReceivePort();
// Notify our creator the port we listen to
sendPort.send(port.sendPort);
final msg = await port.first;
// Execute
final SendPort replyTo = msg[0];
final Function myFunc = msg[1];
final List<dynamic> parameters = msg[2] ?? [];
try {
switch (parameters.length) {
case 0:
replyTo.send(myFunc());
break;
case 1:
replyTo.send(myFunc(parameters[0]));
break;
case 2:
replyTo.send(myFunc(parameters[0], parameters[1]));
break;
case 3:
replyTo.send(myFunc(parameters[0], parameters[1], parameters[2]));
break;
case 4:
replyTo.send(
myFunc(parameters[0], parameters[1], parameters[2], parameters[3]));
break;
case 5:
replyTo.send(myFunc(parameters[0], parameters[1], parameters[2],
parameters[3], parameters[4]));
break;
default:
replyTo.send(Exception("Unsupported argument length"));
}
} catch (err) {
replyTo.send(Exception(err.toString()));
}
// Done
port.close();
Isolate.current.kill();
}
https://github.com/vocdoni/dvote-dart/blob/main/lib/util/asyncify.dart#L16
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
I want to do something after a lot of future functions are done, but I do not know how to write the code in dart?
the code is like this:
for (var d in data) {
d.loadData().then()
}
// when all loaded
// do something here
but I don't want to wait for them one by one:
for (var d in data) {
await d.loadData(); // NOT NEED THIS
}
how to write those code in dart?
You can use Future.wait to wait for a list of futures:
import 'dart:async';
Future main() async {
var data = [];
var futures = <Future>[];
for (var d in data) {
futures.add(d.loadData());
}
await Future.wait(futures);
}
DartPad example
Existing answer gives enough information, but I want to add a note/warning.
As stated in the docs:
The value of the returned future will be a list of all the values that were produced in the order that the futures are provided by iterating futures.
So, that means that the example below will return 4 as the first element (index 0), and 2 as the second element (index 1).
import 'dart:async';
Future main() async {
print('start');
List<int> li = await Future.wait<int>([
fetchLong(), // longer (which gives 4) is first
fetchShort(), // shorter (which gives 2) is second
]);
print('results: ${li[0]} ${li[1]}'); // results: 4 2
}
Future<int> fetchShort() {
return Future.delayed(Duration(seconds: 3), () {
print('Short!');
return 2;
});
}
Future<int> fetchLong() {
return Future.delayed(Duration(seconds: 5), () {
print('Long!');
return 4;
});
}
If you want to wait for multiple futures of different types and also support null-safety then you can add a helper function similar to the following.
import 'package:tuple/tuple.dart';
Future<Tuple2<T1, T2>> waitConcurrently<T1, T2>(
Future<T1> future1, Future<T2> future2) async {
late T1 result1;
late T2 result2;
await Future.wait([
future1.then((value) => result1 = value),
future2.then((value) => result2 = value)
]);
return Future.value(Tuple2(result1, result2));
}
In order for this to work you need tuples.
At the moment Dart does not provide tuples natively, but there is a package from Google which does: https://pub.dev/packages/tuple
In addition, I'd like to supplement Günter Zöchbauer's answer with FutureOr variant. You'll need to convert your FutureOr<T> variable to Future<T> first and then call wait:
Future.wait(list.map((x) async => x))