Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between Future.error and throw in dart Future?

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.

like image 288
Félix Simões Avatar asked Sep 17 '25 23:09

Félix Simões


1 Answers

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.

like image 135
imawizard Avatar answered Sep 23 '25 00:09

imawizard