How to implement Flutter asynchronous processing

takahirom
5 min readMay 3, 2018

--

As most production smartphone applications access DBs and APIs, asynchronous processing is required.

There is two Async Widgets in Flutter Official site.

https://flutter.io/widgets/async/

Since I am a beginner to Dart and Flutter, I do not know which one to use, but first I wrote to use StreamBuilder. After that I found out which one to use depends on the case.

We will describe Dart ‘s asynchronous processing before Flutter’ s writing method.

Dart asynchronous processing

Dart has many kinds of asynchronous writing style.

All the same results from ① to ⑦. I was confused when I saw this.

To conclude earlier, it can be decided as follows.

In this example, ③ can be executed in parallel and efficiently.

I will explain why it looks like this figure.

Future or Stream

Async in Dart (Dart Developer Summit 2015)

You can find this slide.

The API used depends on the number to be returned like this.

If you hit a fixed API and just display the result, use Future. If you change the display by a UI event like a click, use Stream.

Since Stream can also return one value, it can also make it all Stream. However, it is necessary to think about how many values will be returned when using that function, and wasteful code increases. Therefore, I think that you should use something more suitable for your situation.

Future<String> asyncFunc() async {
return "async return";
}
var asyncResult = await asyncFunc();
print(asyncResult);
// vsStream<String> streamFunc() async* {
yield "stream return";
}
streamFunc().listen((result) {
print(result);
});

This allows you to choose whether to use FutureBuilder or StreamBuilder depending on your situation.

Let’s know a bit more about what Dart’s asynchronous processing should be used.

“await for” or “listen()”

In most cases, it is easier to write using wait for.

import 'dart:async';
import 'dart:io';
import 'dart:math';

main() async {
await for (var tick in endlessCountDown()) {
print("coutDown $tick");
}
// endlessCountDown().listen((tick) {
// print("coutDown $tick");
// });
}

Stream<int> endlessCountDown() async* {
var i = pow(2, 30) - 1;
while (true) {
sleep(new Duration(seconds: 1));
yield i--;
}
}

Let’s see an example where there are two streams as follows.

If you want to listen two streams, you can not see “countUp” logs.

Because await for does not move to the next code unless stream ends.

import 'dart:async';
import 'dart:io';
import 'dart:math';

main() async {
await for (var tick in endlessCountDown()) {
print("coutDown $tick");
}
await for (var line in endlessCountUp()) {
print("countUp $line");
}
}

Stream<int> endlessCountUp() async* {
var i = 0;
while (true) {
sleep(new Duration(seconds: 1));
yield i++;
}
}

Stream<int> endlessCountDown() async* {
var i = pow(2, 30) - 1;
while (true) {
sleep(new Duration(seconds: 1));
yield i--;
}
}

You can listen each by separating methods as follows. But… is this easy to read?🤔

import 'dart:async';
import 'dart:io';
import 'dart:math';

main() {
listenCountDown();
listenCountUp();
}

listenCountDown() async {
await for (var tick in endlessCountDown()) {
print("coutDown $tick");
}
}

listenCountUp() async {
await for (var tick in endlessCountUp()) {
print("coutUp $tick");
}
}
...

In this case, it is better to simply use listen().

import 'dart:async';
import 'dart:io';
import 'dart:math';

main() {
endlessCountDown().listen((tick) {
print("coutDown $tick");
});
endlessCountUp().listen((tick) {
print("coutUp $tick");
});
}
...

If you write it with await for, and if it seems to be redundant, you’d better use listen().

You can check official document about this.

A Tour of the Dart Libraries

“await” or “then()” or “await Future.wait()”

// ①
asyncFunc1().then((_) => asyncFunc2()).then((result) {
print(result);
});

// ②
var asyncResult1 = await asyncFunc1();
var asyncResult2 = await asyncFunc2();
print(asyncResult2);

// ③
var list = await Future.wait([asyncFunc1(), asyncFunc2()]);
print(list[1]);

Let’s start with “await” or “then ()” (① or ②).

It is mentioned in the Effective Dart as follows.

The async/await syntax improves readability and lets you use all of the Dart control flow structures within your async code.

I think if you can use async/await, you should use it. So, Let’s use await.

Next let’s look at “await” and “await Future.wait()”.

// ②
Stopwatch stopwatch1 = new Stopwatch()..start();
var asyncResult1 = await asyncFunc1();
var asyncResult2 = await asyncFunc2();
print(asyncResult2);
print('await() executed in ${stopwatch1.elapsed}');

// ③
Stopwatch stopwatch2 = new Stopwatch()..start();
var list = await Future.wait([asyncFunc1(), asyncFunc2()]);
print(list[1]);
print('Future.wait() executed in ${stopwatch2.elapsed}');
Future<String> asyncFunc1() async {
return new Future.delayed(const Duration(seconds: 1), () => "async return1");
}

Future<String> asyncFunc2() async {
return new Future.delayed(const Duration(seconds: 1), () => "async return2");
}

As you can see from this comparison, if each Future can be executed at the same time, it is better to use Future.wait() as it can be executed earlier.

How can I use FutureBuilder?

You can check source code

sample app
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new FutureBuilder(future: new Future(() async {
var token = await AccessToken.getInstance().getOrCreate();
var user = await fetchUser(token);
return await fetchEvents(user, token);
}), builder: (BuildContext context, AsyncSnapshot<EventList> feedState) {
if (feedState.error != null) {
// TODO: error handling
}
if (feedState.data == null) {
return new Center(child: new CircularProgressIndicator());
}
return new MyHomePage(
title: 'Flutter Demo Home Page', events: feedState.data);
}),
// This trailing comma makes auto-formatting nicer for build methods.
);

Conclusion

You can decide which asynchronous mechanism to use according to this chart.
Since I am a Flutter Beginner, please point out anything if I make something mistake.

--

--