I'd like to understand when to use throw
and return Future.error
in async functions. Are there any underlying differences that we should take in consideration when choosing one or the other?
I did a small test:
Future testFuture() async {
return Future.error('error from future');
}
void testThrow() {
throw('error from throw');
}
Future testFutureThrow() async {
throw('error from future throw');
}
main() async {
print('will run test');
try{
await testFuture();
}
catch(e){
print(e);
}
try{
testThrow();
}
catch(e){
print(e);
}
try{
await testFutureThrow();
}
catch(e){
print(e);
}
testFuture().catchError((e) => print(e));
testFutureThrow().catchError((e) => print(e));
print('did run test');
}
They both seem to work in the same way. This is the output:
will run test
error from future
error from throw
error from future throw
did run test
error from future
error from future throw
What we can see from this test is that, from the point of view of the caller, when call the function using try/catch
the code runs synchronous and if we Future.catchError
it's asynchronous. But from the point of view of the function flow it appears to be the same.
NOTE: I had this initially as a response to a question. But then I realised that I was not answering but instead doing a question.
async and await are a language extension/syntactic sugar to simplify working with asynchronous code by restoring a synchronous fashion.
Both abstract away working with Futures directly, making this code analogous:
// On the callee's side
Future<String> calleeAsync() async {
return "from async callee";
}
Future<String> calleeSync() {
return Future.value("from sync callee");
}
// On the caller's side
Future<void> callerAsync() async {
print("got ${await calleeAsync()}");
print("got ${await calleeSync()}");
}
void callerSync() {
calleeAsync()
.then((v) => print("got3 $v"))
.then((_) => calleeSync())
.then((v) => print("got4 $v"));
}
(while in callerSync the second then()
's callback could be merged with the first's, and the third then()
could also be attached directly to calleeSync()
because of monad-like associativity)
In the same manner, these functions are equivalent:
// On the callee's side
Future<void> calleeAsync() async {
throw "error from async";
}
Future<void> calleeSync() {
return Future.error("error from sync");
}
// On the caller's side
Future<void> callerAsync() async {
try {
await calleeAsync();
} catch (e) {
print("caught $e");
}
try {
await calleeSync();
} catch (e) {
print("caught $e");
}
}
void callerSync() {
calleeAsync()
.catchError((e) => print("caught $e"))
.then((_) => calleeSync())
.catchError((e) => print("caught $e"));
}
In summary, there is no practical difference. return Future.error(…)
simply doesn't make use of the extensions an async function provides, thus it's more like mixing two flavours of asynchronous code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With