I want to convert sync method to run as asynchronous
Simple example :
Future<void> increment() async {
for (var i = 0; i < 100000000; i++) {
_counter++;
}
}
When I use this code with flutter the app will freeze, because the code content is running as sync, so now I want to know how can I make this code run as async?
i tried to add Future.delayed as following :
Future<void> increment() async {
for (var i = 0; i < 100000000; i++) {
_counter++;
await Future.delayed(const Duration(microseconds: 1));
}
}
But in some scenarios, it will takes too long time!
Is there a better solution?
Use Isolates in Dart for heavy calculations
There is compute constant in Flutter api for top-level and static functions who manage with Isolates by themselves.
Look into this page Concurrency in Dart
Paste the following code into a test.dart file, and see how you can create an async method from scratch using isolates.
import 'dart:io';
import 'dart:isolate';
void main() async {
// Read some data.
final result = await Isolate.run(_readAndParseDart);
print("Called the async function");
}
String _readAndParseDart() {
final fileData = File("test.dart").readAsStringSync();
print('Finished the asynchronous code');
return fileData;
}
Also try this code and notice the difference (which result will be printed first) when we do not use the async/await when calling the asynchronous method:
import 'dart:io';
import 'dart:isolate';
void main() {
// Read some data.
final result = Isolate.run(_readAndParseDart);
print("Called the async function");
}
String _readAndParseDart() {
final fileData = File("test.dart").readAsStringSync();
print('Finished the asynchronous code');
return fileData;
}
Related
void main() async {
check();
print('end');
}
Future check() async {
var version = lookUpVersion();
print(version);
}
int lookUpVersion() {
return 12;
}
void main() async {
check();
print('end');
}
Future check() async {
var verion = await lookUpVersion();
print(version);
}
int lookUpVersion() {
return 12;
}
These two code only have one difference, await keyword.
I wonder that why not did they wait for main function code? even I used Future+async keyword at first code.
Can you explain about this?
The async and await keywords provide a declarative way to define asynchronous functions and use their results.
For first one - result will be
//12
//end
For second one - result will be
//end
//12
Which means if you add await it will become asynchronous.
I am writing a basic programme to teach myself Dart/Flutter. Part of the programme uses the http.dart package to get some data and the http.get command returns a Future value. In order to unpack this value, I need to use an await command, which then changes the execution order of my code. I cannot work out how to preserve the intended execution order whilst using async/await. I am new to this, so appreciate that I am probably missing something obvious.
Code example 1 below uses async/await through a series of functions. This approach gives more or less the correct output order (other than the end of main()), but would mean (I think) that I would need to have an async build() method, which is not valid in Flutter.
// Cascading async methods with local variables and await statements
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() async {
print('build: Before getName.');
String name = await _getName();
print('build: After getName.');
}
// Get some data, make some decisions
Future<String> _getName() async {
print('getName: before getData');
String name = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: returning body.data');
return name;
} else {
print('getName: returning Bob');
return 'Bob';
}
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
The output from this is (I have truncated the html data that is returned):
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
Main: end.
getData: After http get.
getName: after getData
getName: returning body.data
build: After getName. Name is: <html data>
The second code example below uses a global variable to capture data in the _getName() method so that I can avoid using async/await in the build() method. This does not give the correct execution order or the correct output.
// Use global variable to receive awaited data and avoid cascading async methods
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
The output from this code is shown below. Note that the build() method completed before _getData and _getName and the name printed in build() is empty in the 5th row.
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = body.data
In the third example below, I have tried using .then to ensure that the code in each function only executes after the await command. I didn't think this would work (and it didn't) because I think I have a problem controlling the flow between functions, not a problem controlling the flow within functions, but I thought I should give it a go and I was clutching at straws by this point.
// Use global variable to avoid using await in build() method
// Use .then to ensure that method actions follow await command
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = ""; // Global variable for async data return
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
await _getData().then((data) {
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
});
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
String value = "";
await http.get(Uri.parse('http://www.google.co.uk')).then((data) {
print('getData: After http get.');
value = data.body;
});
return value;
}
The output from this code is shown below. As with the second example, the execution is not in the correct order and the name printed in the build() method is empty.
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = Bob
Ideally, the output from the programme should be:
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
getData: After http get.
getName: after getData
getName: setting name = Bob
build: After getName. Name is: Bob
Main: end.
How do I write my code so that I can use the http.get method and ensure that my code executes in the order that I want? I'll just add that I have read A LOT of stackoverflow questions, flutter documentation and general help online, but not found anything that answers my question so far.
Or nothing that I understand. :D Apologies if this is a stupid question. I am a noob at this.
I should have added that this example is an simplification of the problem in a Flutter app I am writing (noughts and crosses). This is checking for a win/draw after each move, then reading data from a DB, updating the results and writing them back to the DB. It also updates the game state to show that the game is over. The problem caused by async/await is that gamestate isn't being updated whilst the functions await the data and the game continues in the "playing" state even though the game is over. Pseudo code of the programme below (this is a bit scrappy, but hopefully it illustrates the problem).
build() {
checkState(win, draw or continue?);
if (continue){
_humanMove();
_computerMove();
drawTheWidgets;
} else {
drawDifferentWidgets; // New game
}
}
void _humanMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
void _computerMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
results _updateResults() async {
await http.get data results in database;
updateWithResult;
await http.put data results in database;
}
You need to wait ("await") for all the function calls, see:
void main(List<String> arguments) async {
print('Main: start.');
await _build();
print('Main: end.');
}
// Draw some stuff, make some decisions
Future<void> _build() async {
print('build: Before getName.');
await _getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await Future.delayed(const Duration(milliseconds: 500), () {
return 'x';
});
print('getName: after getData');
double val = 5;
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
One comment: I believe it's easier for you to learn to do this right, if you try to learn this not based on console output, but with a Flutter app. You would see that your program goes through different states that actually need time - before, during and after your http request.
In your build method of a widget, you would need to provide something to show for for each state of your program. So you actually do not wait (await) for results, but update the state based on the results. And depending on that, your build method is prepared to take the different states and show something adequate. Then async/await is quite nice.
--- adding the following in response to the clarifying comment ---
The build method of a widget is not where you put business logic of your app. Build is called whenever a widget is rebuild (if you have a http request writing in a database, it would be triggered a whole number of times!). Trying to apply the steps you wanted your app to do in your first description with the intended output on the console, I tried to write this clarifying app (and to have different states that your app passes through I included that you have to click the icon button / FloatingActionButton to trigger the process). Please have a look how the build method of the second widget deals with all the different states (it prints them to the console and shows a text - you could do more fancy stuff there of course, based on what the state of your app implies). The real "action" (changes of the state of your app) is happening elsewhere:
import 'package:flutter/material.dart';
class ExampleWidget extends StatefulWidget {
const ExampleWidget({Key? key}) : super(key: key);
#override
_ExampleWidgetState createState() => _ExampleWidgetState();
}
class _ExampleWidgetState extends State<ExampleWidget> {
String _showWhatsHappening = 'Before getName.';
Future<String> _getData() async {
setState(() {
_showWhatsHappening = 'getData: Before http get.';
});
final data = await Future.delayed(const Duration(milliseconds: 500), () {
return 'Bob';
});
setState(() {
_showWhatsHappening = 'getData: After http get.';
});
await Future.delayed(const Duration(milliseconds: 300));
return data;
}
#override
void initState() {
print('not main, but initstate: start.');
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(brightness: Brightness.light, fontFamily: 'Example'),
home: Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.star_outline),
onPressed: () async {
setState(() {
_showWhatsHappening = 'before getData ';
});
final x = await _getData();
await Future.delayed(const Duration(milliseconds: 300));
setState(() {
_showWhatsHappening = 'after getData ';
});
await Future.delayed(const Duration(milliseconds: 300));
setState(() {
_showWhatsHappening = 'setting name = $x';
});
},
),
body: Center(child: ShowItWidget(showIt: _showWhatsHappening)),
),
);
}
}
class ShowItWidget extends StatelessWidget {
final String showIt;
const ShowItWidget({Key? key, required this.showIt}) : super(key: key);
#override
Widget build(BuildContext context) {
print(showIt);
return Text(showIt);
}
}
void main() {
runApp(const ExampleWidget());
}
For completeness, here is my Dart-specific solution using Streams.
// Use global variable to receive awaited data
// Use StreamController to wait for async methods to finish
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
StreamController streamController = StreamController.broadcast();
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
streamController.stream.listen((args) {
print('build: After getName. Name is: $name');
});
}
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data.length.toString();
} else {
print('getName: setting name = Bob');
name = 'Bob ${data.length.toString()}';
}
print('gateName. Name is $name');
streamController.add(name);
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}
In the beginning of system ,I need to load the xml file.
I use async/await to wait for rootBundle.loadString finish.
However in this case, print ("I can wait rootBundle here"); is executed after rootBundle.loadString finish, runApp(MyApp()) starts before finished.
Is there a way to execute runApp() after rootBundle.loadString finish
Because my whole setting is written in one xml files. so I want to make sure to load before first building.
void main(){
inter = new Internationalize();
inter.init();
runApp(MyApp());
}
class Internationalize{
var intls = {};
var xmlBody;
void init(){
print("internationalize class start");
rootBundle.loadString('assets/i18n/en/strings.xml').then(
(String contents) {
var document = xml.parse(contents);
xmlBody = document.findAllElements('string');
print("load finish");
});
print ("I can wait rootBundle here");
}
}
void main() async { // mark it async
WidgetsFlutterBinding.ensureInitialized(); // mandatory since Flutter 1.9 if you're making this method async
inter = new Internationalize();
await inter.init(); // await here
runApp(MyApp());
}
class Internationalize {
var intls = {};
var xmlBody;
Future<void> init() async { // make it Future<void>
print("internationalize class start");
String contents = await rootBundle.loadString('assets/i18n/en/strings.xml'); // await on loadString
var document = xml.parse(contents);
xmlBody = document.findAllElements('string');
print("load finish");
print("I can wait rootBundle here");
}
}
This is the same answer as given by #CopsOnRoad with some explanation.
You should separate the use of future response rootBundle.loadString().
try following:
String contents = await rootBundle.loadString('assets/i18n/en/strings.xml');
var document = xml.parse(contents);
xmlBody = document.findAllElements('string');
print("load finish");
Note: you'll need to add async modifier in all methods, even in the main method as well and add await before every method call.
i.e.:
void main(){
...
await inter.init();
...
}
...
Future<void> init async {
String contents = await rootBundle.loadString('assets/i18n/en/strings.xml');
var document = xml.parse(contents);
xmlBody = document.findAllElements('string');
print("load finish");
}
I have a dynamic list of strings including codes.
I want to fetch events for each code and then yield to next state.
I have tried Future.wait() but since is dynamic I can not pass a list of async calls.
This is my code so far:
var packagesData = List<BuiltList<Event>>();
for (var packageNumber in packages) {
final packageEvents = await serviceDataSource.fetchPackageEvents(packageNumber);
packagesData.add(packageEvents);
if (packagesData.length == packages.length) {
return persistAllPackagesData(packagesData);
}
}
I wanted to do something similar to a Promise:
Promise.all([
// for loop here
]).then(() => {
console.log('I did everything!');
});
Use a taskList to maintain needed task and use Future.wait
List<Future<int>> taskList = [];
taskList.add(getRandomNumber());
...
Future.wait(taskList)
.then
full code
import 'dart:async';
import 'dart:math';
Future<int> getRandomNumber() async {
var random = new Random();
return random.nextInt(100);
}
void findSmallestNumberInList(List<int> lst) {
print("all numbers are in:");
lst.forEach((l) => print(l));
lst.sort();
int largest = lst.first;
print("The smallest random # we generated was: ${largest}");
}
void main() async {
List<Future<int>> taskList = [];
taskList.add(getRandomNumber());
taskList.add(getRandomNumber());
taskList.add(getRandomNumber());
Future.wait(taskList)
.then((List<int> results) => findSmallestNumberInList(results));
Future.wait([getRandomNumber(), getRandomNumber(), getRandomNumber()])
.then((List<int> results) => findSmallestNumberInList(results));
}