Flutter: 'Future.wait' multiple async functions in parallel VS 'await' one at a time. <= different results - flutter

I recently learned of the fabulous way of waiting for multiple async functions to complete using Future.wait([asyncFuncOne(), asyncFunctwo()])
However, I noticed two different outcomes when running either of these blocks of code. One awaiting each function to finish, the other using Future.wait for parallel processing. What am I doing wrong?
Method 1:
await msm.initProfileData();
await msm.initActivityFeed();
await msm.getRecentlyActiveUsers();
await msm.getRecommendedUsers();
await msm.getGroups();
await msm.getFollowing();
await msm.getFollowers();
Method 2:
await Future.wait([
msm.getFollowing(),
msm.initProfileData(),
msm.initActivityFeed(),
msm.getRecentlyActiveUsers(),
msm.getRecommendedUsers(),
msm.getGroups(),
msm.getFollowers(),
]);
in Method 1, all the async functions complete before my apps home screen appears. In Method 2 the home screen appears before all the async functions complete.
Cheers and thanks in advance.
EDIT: Additional code example.
#override
void initState() {
super.initState();
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
setState(() => _showSignIn = true);
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) async {
if (account != null) {
await createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
_showSignIn = true;
});
}
}
createUserInFirestore() async {
final GoogleSignInAccount user = googleSignIn.currentUser;
DocumentSnapshot doc = await usersRef.document(user.id).get();
//...
//do stuff
//...
await someFunc1(); //Method1
// await comeFunc2(); //Method2
//do more stuff
}
someFunc1() async {
msm.asyncfunc1();
msm.asyncfunc2();
}
someFunc2() async {
await Future.wait([
msm.asyncFunc1(),
msm.asyncFunc2(),
]);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}

Using Future.wait(List<Future>) will wait for all the async operations without sequence as mentioned in the docs. While using await consecutively, it'll wait for the first await async operation to finish before running the next await async operation. If you have a prerequisite output before running the next async operation, it's better to use await async in sequence instead.

Related

Semaphore in flutter

Code:
void _test() {
print(1);
Timer.run(() {
print(2);
});
print(3);
}
Print 1 3 2.
I want print 1 2 3.
In iOS I can use Semaphore, how can I do this in flutter?
Awaiting for a method to complete can be written inside a future
void _test() async{
print(1);
Future.delayed(Duration(seconds : 0), (){
print(2);
});
print(3);
}
//Output 1 3 2
void _test() async{
print(1);
await Future.delayed(Duration(seconds : 0), (){
print(2);
});
print(3);
}
//Output 1 2 3
In the second example the await will wait for the future to complete and then move to the next task
Thanks #jamesdlin, I solved with his comment, below is desired code:
void _test() async {
print(1);
Completer<void> completer = Completer();
Timer.run(() {
print(2);
completer.complete();
});
await completer.future;
print(3);
}
Timer.run() basically tells the program that here is a piece of code that should be executed but I don't care if it finishes before it reaches the code outside of this block.
If you want to wait a bit, use Future.delayed:
runAsyncMethod() async {
print("1");
await Future.delayed(Duration(seconds: 3), () {print("2");});
print("3");
}

`Stream.toList` stuck forever in a fake_async environment

void main() {
test('simple', () async {
await fakeAsync((async) async {
final stream = Stream.value(42);
print('hi before await');
await stream.toList();
print('hi after await');
});
});
}
stucks forever in the toList line...
I have also tried to call whatever methods, but still stuck forever in the "await" line
test('simple', () async {
await fakeAsync((async) async {
final stream = Stream.value(42);
final future = stream.toList();
print('hi before await');
async.flushMicrotasks();
async.flushTimers();
async.elapse(const Duration(seconds: 10));
async.elapseBlocking(const Duration(seconds: 10));
await future;
print('hi after await');
});
});
I have looked at the implementation of Stream.toList as follows, but it seems quite normal
Future<List<T>> toList() {
List<T> result = <T>[];
_Future<List<T>> future = new _Future<List<T>>();
this.listen(
(T data) {
result.add(data);
},
onError: future._completeError,
onDone: () {
future._complete(result);
},
cancelOnError: true);
return future;
}

Asynchronous method not running in proper order

I have these methods, for some reason fetchItems is being called first before initPosition, how come dart wont wait for it to finish and proceeds to the second method? I've added async/await but it still doesn't work. I've also checked my backend logs to confirm this. Am I doing something wrong?
Future<void> initPosition() async {
if (_latitude != null && _longitude != null) {
await Socket.updatePosition(
lat: 51,
lon: 17,);
}
}
Future<void> initMarkers() async {
await initPosition();
await Provider.of<Items>(context, listen: false)
.fetchItems();
}
void initMapState() async {
await getCurrentLocation().then((_) async {
await initMarkers();
setState(() {
_loaded = true;
});
});
}
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) {
initMapState();
});
}
Future<void> fetchItems() async {
itemList = await repository.getItemList();
notifyListeners();
}
Working with multiple asynchronous functions inside Futures depends on whether one is finished or not, not every single one. For this, you can call the "whenComplete" method so you can assure that your future function have finished running. Like this:
For your initMarkers() function:
Future<void> initMarkers() async {
await initPosition().whenComplete((){
Provider.of<Items>(context, listen: false)
.fetchItems();
});
}
For your initMapState() function:
void initMapState() async {
await getCurrentLocation().whenComplete(() async {
await initMarkers().whenComplete((){
setState(() {
_loaded = true;
});
});
});
}
Keep in mind that, in your code, you are not working with the returning value of your getCurrentLocation() function, so instead of using the "then" method use the "whenComplete" method, assuring that you changed or returned your values with this function. Finally, for the initState(), make the function body with asynchronous:
#override
void initState() {
super.initState();
_location.enableBackgroundMode(enable: false);
WidgetsBinding.instance?.addPostFrameCallback((_) async {
initMapState();
});
}
This should work.

Use StreamSubscription as Future in Dart/Flutter

I want to connect my Flutter app to bluetooth device with flutter_blue library, and return a result (anything) when the connection is ON. But I don't understand how to do.
Here my code :
Future connect(BluetoothDevice device) async {
_btDevice = device;
StreamSubscription<BluetoothDeviceState> subscription;
subscription = device.state.listen((event) async {
if (event != BluetoothDeviceState.connected) {
await device.connect();
} else {
await device.discoverServices().then((value) => _initFeatures(value));
}
})
..onDone(() {
// Cascade
print("onDone");
});
subscription.asFuture();
//subscription.cancel();
}
And when I call this function with
await newDevice.connect(bluetoothDevice).then((value) => print('OK'));
OK is written before the real connection. _initFeatures if well call when the device is connected.
I try to use asFuture from StreamSubscription with onDone, but that change nothing.
Could you help me please ?
UPDATE 12/10
I've worked on another project for few monthes, and when I come back, I can't solve the problem, so I add the full code.
The concept is a class widget calls the connect future in other class and need to receipt the end of work.
Widget
Future<void> _connectDevice() async {
try {
widget.device.connect(widget.btDevice!).then((value) => _initValue());
} catch (e) {
print(e);
}
}
_initValue() is a method to create the rest of the screen
and the Future connect()
Future connect(BluetoothDevice device) async {
_btDevice = device;
StreamSubscription<BluetoothDeviceState> subscription;
subscription = device.state.listen((event) async {
if (event != BluetoothDeviceState.connected) {
await device.connect();
} else {
await device
.discoverServices()
.then((value) => _initFeatures(value))
.then((value) => print("OK"));
}
});
await subscription.asFuture();
await subscription.cancel();
}
What I'd like is the Future finishes when print("OK") is called, in order .then((value) => _initValue()); is called.
The problem is only this end. Maybe it's not the good way to implement this kind of solution.

Flutter initState wait for async function to complete

in my main.dart i have among others those two functions:
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
}
And
#override
void initState() {
super.initState();
_fetchMasterData();
}
What i would like to have is to wait in initState till _fethcMasterData is done bevore Widgert build is called.
Is that possible? Many thanks for any help!
Here how I use an async func in initstate;
builder() async {
favoriteDatabase =
await $FloorFavoriteDatabase.databaseBuilder('favorite_database.db')
.build();
setState(() {
favoriteDao = favoriteDatabase.favoriteDao;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) =>
getNamePreferences().then(updateName));
});
builder();
favoriteDao.findAllMoviesAsStreamW();
favoriteDao.findAllMoviesAsStream();
}
Also you can check this mini article too.
It is not possible to await in initState, so when you finish all loading process then you can call SetState method which populate your widget with actual data.
Second solution could be use of futurebuilder or streambuilder where you want to show data but it is only possible if any methods data is not dependent on each other.
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
SetState((){}); // added line
}