The following Dart code totally functional:
Future<Result<bool, String>> downloadFileFrom(String url) async {
try {
const defaultDir = '/sdcard/Download';
final [targetDir as String, hasPermission as bool] = await Future.wait([
getExternalStorageDirectory().then((storage) => storage?.path ?? defaultDir),
_checkPermission(),
]);
return !hasPermission
? const Ok(false)
: FlutterDownloader.enqueue(
url: url,
savedDir: targetDir,
showNotification: true,
openFileFromNotification: true,
saveInPublicStorage: true,
).then((taskId) => Ok(taskId != null))
;
} catch (e) {
return Err(e.toString());
}
}
I wish to know there is any difference when returning FlutterDownloader.enqueue
with wait or without it. If I shouldn't use wait, then shouldn't I return Future.value(Ok(false))
too?
From @Abion47's and @Rico's answers I wanted to go a little deeper into the topic, so I generated the following code:
Future<String> ffuncA([bool useConst = false, int ms = 1000, bool throwError = false]) async {
return useConst
? 'const_A'
: await Future.delayed(
Duration(milliseconds: ms),
() {
if (throwError) throw Exception('Error in ffuncA');
return 'value_A ($ms)';
}
);
}
Future<String> ffuncB([bool useConst = false, int ms = 1000, bool throwError = false]) async {
return useConst
? 'const_B'
: Future.delayed(
Duration(milliseconds: ms),
() {
if (throwError) throw Exception('Error in ffuncB');
return 'value_B ($ms)';
}
);
}
Future<String> ffuncC([bool useConst = false, int ms = 1000, bool throwError = false]) {
return useConst
? Future.value('const_C')
: Future.delayed(
Duration(milliseconds: ms),
() {
if (throwError) throw Exception('Error in ffuncC');
return 'value_C ($ms)';
}
);
}
List<int> getTimes(bool flag) => flag ? List.filled(3, 1000) : List.generate(3, (i) => 1500 - i*250);
Future<void> withAwait({bool useConst = false, bool useDefaultTime = true, bool throwError = false}) async {
try {
final times = getTimes(useDefaultTime);
print('Calling futures...');
print('... A');
await ffuncA(useConst, times[0], throwError).then(print);
print('... B');
await ffuncB(useConst, times[1], throwError).then(print);
print('... C');
await ffuncC(useConst, times[2], throwError).then(print);
print('Done!');
} catch (e) {
print('Error catched in try/catch block: $e');
}
}
void withoutAwait({bool useConst = false, bool useDefaultTime = true, bool throwError = false}) {
try {
final times = getTimes(useDefaultTime);
print('Calling futures...');
print('... A');
ffuncA(useConst, times[0], throwError).then(print);
print('... B');
ffuncB(useConst, times[1], throwError).then(print);
print('... C');
ffuncC(useConst, times[2], throwError).then(print);
print('Done!');
} catch (e) {
print('Error catched in try/catch block: $e');
}
}
Future<void> withAwaitOnError({bool useDefaultTime = true}) async {
try {
final times = getTimes(useDefaultTime);
print('Calling futures...');
print('... A');
await ffuncA(false, times[0], true).then(print).onError((e, _) => print('Error catched with onError: $e'));
print('... B');
await ffuncB(false, times[1], true).then(print).onError((e, _) => print('Error catched with onError: $e'));
print('... C');
await ffuncC(false, times[2], true).then(print).onError((e, _) => print('Error catched with onError: $e'));
print('Done!');
} catch (e) {
print('Error catched in try/catch block: $e');
}
}
void withoutAwaitOnError({bool useDefaultTime = true}) {
try {
final times = getTimes(useDefaultTime);
print('Calling futures...');
print('... A');
ffuncA(false, times[0], true).then(print).onError((e, _) => print('Error catched with onError: $e'));
print('... B');
ffuncB(false, times[1], true).then(print).onError((e, _) => print('Error catched with onError: $e'));
print('... C');
ffuncC(false, times[2], true).then(print).onError((e, _) => print('Error catched with onError: $e'));
print('Done!');
} catch (e) {
print('Error catched in try/catch block: $e');
}
}
The code for main function will be something like this:
void main(List<String> args) {
print('>>>> BEGIN MAIN');
// code line here!!!!
print('>>>> END MAIN');
}
Let's try every case:
withAwait();
>>>> BEGIN MAIN
Calling futures...
... A
>>>> END MAIN
value_A (1000)
... B
value_B (1000)
... C
value_C (1000)
withoutAwait();
>>>> BEGIN MAIN
Calling futures...
... A
... B
... C
>>>> END MAIN
value_A (1000)
value_B (1000)
value_C (1000)
withAwait(useDefaultTime: false);
>>>> BEGIN MAIN
Calling futures...
... A
>>>> END MAIN
value_A (1500)
... B
value_B (1250)
... C
value_C (1000)
withoutAwait(useDefaultTime: false);
>>>> BEGIN MAIN
Calling futures...
... A
... B
... C
>>>> END MAIN
value_C (1000)
value_B (1250)
value_A (1500)
withAwait(throwError: true);
>>>> BEGIN MAIN
Calling futures...
... A
>>>> END MAIN
Error catched in try/catch block: Exception: Error in ffuncA
withoutAwait(throwError: true);
>>>> BEGIN MAIN
Calling futures...
... A
... B
... C
>>>> END MAIN
Unhandled exception:
Exception: Error in ffuncA
#0 ffuncA.<anonymous closure> (file:///home/.../demo/main.dart:7:27)
#1 new Future.delayed.<anonymous closure> (dart:async/future.dart:419:42)
#2 Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#3 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:410:19)
#4 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:441:5)
#5 _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)
Exited (255).
withAwaitOnError();
>>>> BEGIN MAIN
Calling futures...
... A
>>>> END MAIN
Error catched with onError: Exception: Error in ffuncA
... B
Error catched with onError: Exception: Error in ffuncB
... C
Error catched with onError: Exception: Error in ffuncC
withoutAwaitOnError();
>>>> BEGIN MAIN
Calling futures...
... A
... B
... C
>>>> END MAIN
Error catched with onError: Exception: Error in ffuncA
Error catched with onError: Exception: Error in ffuncB
Error catched with onError: Exception: Error in ffuncC
Now I know there are three ways to write a future return, and when to use onError
. Therefore my original code just needs to add onError
toFlutterDownloader.enqueue
and it would be correct.