Reputation: 356
I have written an async Flutter/Dart function which behaves unexpectedly in my opinion. Following code structure:
static Future<bool> verifySometing() async {
try {
await getCloudData().then((snapshot) {
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
});
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
The expected result would be that the function awaits the cloud data, then awaits validation and returns true if the data is valid. In this case, the console would show the following and the function would return true:
Data is correct
What happens in practice is that the console shows the following output and the function first returns true and then false:
Data is correct
Something went wrong
This goes against anything I thought to know about funtions in Dart because I always assumed that once return is fired, the function is done. Any ideas how this happens?
Upvotes: 1
Views: 974
Reputation: 24811
There are a few of faults in your assumptions.
First, you've attached a then
to the future returned by getCloudData()
. This means that you aren't awaiting getCloudData()
, but instead the additional future returned by getCloudData().then(...)
, and that future will return when the callback function passed to it completes. (And unless the first future throws an error, the callback will be called.)
Second, the callback function operates on its own scope. So this code is not doing what you think it's doing:
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
This return will affect the callback function, not the verifySomething
function.
Given these, the order of operation is as follows:
validateSomething
function await
s getCloudData().then(...)
.getCloudData()
gets called.getCloudData()
returns, the callback passed to then
is called.validateData
is called.true
.validateSomething
is notified that the awaited future is complete, so execution resumes.validateSomething
function returns false
.Generally speaking, these kinds of errors are common when mixing async/await
and then
patterns. Unless you know what you're doing, stick with either one or the other, preferably the async/await
pattern. For example, a refactor of your code to eliminate the call to then
is as follows:
static Future<bool> verifySometing() async {
try {
final snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
Now that there isn't a pesky closure to deal with, return
will return from validateSomething
as expected and you don't need to deal with issues like callbacks and scope.
Upvotes: 1
Reputation: 6430
The issue is with this line.
await getCloudData().then((snapshot) {
Here, instead of just await
ing, you have also attached a then
callback. So in actuality, whatever you are returning is return value of the callback
function ie., (snapshot) {}
.
The callback function that you need to pass into the then
takes that return true
and gives it to us as the result of await
.
So, if you would've put something like
var bool = await getCloudData().then((snapshot) { ..... });
Then this bool
, would've been equal to true
. That's it. No return from the your main function.
Change it to this,
var snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
Hope, I was able to explain clearly.
Upvotes: 1