I came across a behaviour I am unable to work around. Basically, I am trying to fetch data, and return a Future
of these data, where some of the requests can fail but we can ignore them.
This is my idea so far : for each data we want to get, create a future that returns null if it fails, and the value otherwise. Then wait all the futures, and remove null values.
Here is my code : (simple test data)
Future<List<String>> testFunc() {
// create a result list
List<Future<String?>> result = List.empty(growable: true);
// put 10 future in the results
for(int i = 0; i < 10; i++) {
result.add(
// create a future that may fail
Future.delayed(Duration(seconds: i), () {
if(i % 2 == 0) { return i; }
else { throw Error(); }
})
// if the future succeed, return the value
.then((value) {
return "got from future : $value";
})
// otherwise, returns "null" as we are expecting a future returning a nullable value
.catchError((error) {
return null; // <========= Linter error here
})
);
}
// then, wait for all futures, and remove the ones that crashed
return Future.wait(result).then((listOfNullable) => listOfNullable.where((element) => element != null).map((e) => e!).toList());
}
When I run this and one of the futures fails and return null, I'm having an error :
The error handler of Future.catchError must return a value of the future's type
which I don't understand, as null is a valid value for String?
?
Something that does work is with explicit casts :
Future<List<String>> testFunc() {
// create a result list
List<Future<String?>> result = List.empty(growable: true);
// put 10 future in the results
for(int i = 0; i < 10; i++) {
result.add(
// create a future that may fail
Future.delayed(Duration(seconds: i), () {
if(i % 2 == 0) { return i; }
else { throw Error(); }
})
// if the future succeed, return the value
.then((value) {
return "got from future : $value" as String?; // <========= Linter error here
})
// otherwise, returns "null" as we are expecting a future returning a nullable value
.catchError((error) {
return null as String?; // <========= Linter error here
})
);
}
// then, wait for all futures, and remove the ones that crashed
return Future.wait(result).then((listOfNullable) => listOfNullable.where((element) => element != null).map((e) => e!).toList());
}
But now, the linter tells me I have an unnecessary cast, and I'm trying to remove any linter errors.
What I am missing here ?
The linter errors are caused by the call to then(...)
, which the dart linter eagerly resolves to then<String>
instead of then<String?>
.
You can specify the type explicitly to work around this behavior:
Future<List<String>> testFunc() {
List<Future<String?>> result = List.empty(growable: true);
for(int i = 0; i < 10; i++) {
result.add(
Future.delayed(Duration(seconds: i), () {
if(i % 2 == 0) { return i; }
else { throw Error(); }
})
.then<String?>((value) { // <- Change here!
return "got from future : $value";
})
.catchError((error) {
return null; // No more linter warning
})
);
}
// then, wait for all futures, and remove the ones that crashed
return Future.wait(result).then((listOfNullable) => listOfNullable.where((element) => element != null).map((e) => e!).toList());
}