7
votes

I'm building a user authentication module for my app and I am running into trouble with some asynchronous code.

Firstly, here is the error that is thrown:

E/flutter (17162): [ERROR:flutter/shell/common/shell.cc(188)] Dart Error: Unhandled exception: E/flutter (17162): 'dart:async/future_impl.dart': Failed assertion: line 146: 'optimized out': is not true. E/flutter (17162): #0 _AssertionError._doThrowNew (dart:core/runtime/liberrors_patch.dart:40:39) E/flutter (17162): #1 _AssertionError._throwNew (dart:core/runtime/liberrors_patch.dart:36:5) E/flutter (17162): #2 _FutureListener.handleError (dart:async/future_impl.dart:146:14) E/flutter (17162): #3 Future._propagateToListeners.handleError (dart:async/future_impl.dart:654:47) E/flutter (17162): #4 Future._propagateToListeners (dart:async/future_impl.dart:675:24) E/flutter (17162): #5 Future._completeError (dart:async/future_impl.dart:494:5) E/flutter (17162): #6 _SyncCompleter._completeError (dart:async/future_impl.dart:55:12) E/flutter (17162): #7 _Completer.completeError (dart:async/future_impl.dart:27:5) E/flutter (17162): #8 _AsyncAwaitCompleter.completeError (dart:async/runtime/libasync_patch.dart:40:18) E/flutter (17162): #9 FirebaseAuth.signInWithEmailAndPassword (package:firebase_auth/firebase_auth.dart) E/flutter (17162): E/flutter (17162): #10 Session.login. (package:mood_map/utilities/session.dart:31:24) E/flutter (17162): #11 _RootZone.runUnary (dart:async/zone.dart:1379:54) E/flutter (17162): #12 _FutureListener.handleValue (dart:async/future_impl.dart:129:18) E/flutter (17162): #13 Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:642:45) E/flutter (17162): #14 Future._propagateToListeners (dart:async/future_impl.dart:671:32) E/flutter (17162): #15 Future._complete (dart:async/future_impl.dart:476:7) E/flutter (17162): #16 _SyncCompleter.complete (dart:async/future_impl.dart:51:12) E/flutter (17162): #17 _AsyncAwaitCompleter.complete (dart:async/runtime/libasync_patch.dart:28:18) E/flutter (17162): #18 _completeOnAsyncReturn (dart:async/runtime/libasync_patch.dart:295:13) E/flutter (17162): #19 Session._checkUserAlreadyExists (package:mood_map/utilities/session.dart) E/flutter (17162): E/flutter (17162): #20 Session.login (package:mood_map/utilities/session.dart:27:11)

And here are the functions that are involved:

static final FirebaseAuth _authenticator = FirebaseAuth.instance;

static void login(BuildContext context, String email, String password) async {

email = email.trim();
password = password.trim();

//Check if the user already exists
await _checkUserAlreadyExists(email).then((exists) {

  if(exists) {

    _authenticator.signInWithEmailAndPassword(email: email, password: password)
        .then((FirebaseUser user) { _loginSuccess(); })
        .catchError((Error e) { _loginFailure(context); });

  } else {

    Utilities.showMessageDialog(context, "That user doesn't exist. Please create an account below.");

  }

});

} 

----------------------------------------------------------------------

static Future createUserAccount(BuildContext context, email, String password) async {

//Check if the user already exists
await _checkUserAlreadyExists(email).then((exists) {

  if(exists) {

    Utilities.showMessageDialog(context, "That user already exists. Please login or select another account.");
    AppNavigator.navigateToLoginScreen();

  } else {

    _authenticator.createUserWithEmailAndPassword(email: email, password: password)
        .then((FirebaseUser user) { _createUserSuccess(); })
        .catchError((Error e) { _createUserFailure(context); });

  }

});

}

In short, the call to _authenticator.signonWithEmailAndPassword() is failing. I know that the _authenticator instance is working with other functions so I know it isnt a problem with Firebase itself.

I am worried that I am doing something incorrectly by calling another asynchronous function, _authenticator.signonWithEmailAndPassword() from within another asynchronous function, _checkIfUserAlreadyExists(). It seems that this should be okay to do from within a .then() block from what I've read but the error message seems pretty insistent that it is something to do with the setup of the asynchronous nature of the function calls.

Thoughts?

2
Did you get the solution for the above problem?Satya Attili
No. I did more research and it seems to be a systemic issue with the current version of Flutter. I found a github issue reporting it so the developers are aware. I posted my findings as well. See: github.com/flutter/flutter/issues/23512jared-nelsen

2 Answers

6
votes

If you use .then() clauses don't use await.

.then() and await are two different ways to handle Future's but shouldn't be used for the same Future instance.

1
votes

Consider using async - await to catch the errors in the 'final step'. This answer https://github.com/flutter/flutter/issues/22734 helped me a lot.

Below is a code snippet I got from a source I can't remember but it helped me understand how to properly work with Futures. I modified it a little bit to test for my exact situation (added main4() and divideFullAsyncNested() functions). I hope it helps.

// SO 29378453
import 'dart:async';

import 'package:login_app/constants.dart';

main() {
  // fails
  main1();
  main2();
  main3();
  main4();
}

Future<double> divide(int a, b) {
  // this part is still sync
  if (b == 0) {
    throw new Exception('Division by zero divide non-async');
  }
  // here starts the async part
  return new Future.value(a / b);
}

Future<double> divideFullAsync(int a, b) {
  return new Future(() {
    if (b == 0) {
      throw new Exception('Division by zero full async');
    }
    return new Future.value(a / b);
    // or just
    // return a / b;
  });
}

Future<double> divideFullAsyncNested() {
  return divideFullAsync(7, 8).then(
    (val) {
      return divideFullAsync(5, 0).then(
        (val2) {
          return Future(() {
            if (val2 == 1) {
              throw Exception('Innermost: Result not accepted exception.');
            }
            return val2;
          });
        },
      ).catchError((err) => throw Exception('Inner:    $err'));
    },
  ).catchError((err) => throw Exception('Outter: $err'));
}

//Future<double> divideFullAsyncNested() {
//  return divideFullAsync(9, 9).then(
//    (val) {
//      return Future(
//        () {
//          if (val == 1) {
//            throw Exception('Result not accepted exception.');
//          }
//          return val;
//        },
//      );
//    },
//  ).catchError((err) => throw Exception(err.toString()));
//}

// async error handling doesn't catch sync exceptions

void main1() async {
  try {
//    divide(1, 0).then((result) => print('(1) 1 / 0 = $result')).catchError(
//        (error) => print('(1)Error occured during division: $error'));
    var result = await divide(1, 0);
    print('(1) 1 / 0 = $result');
  } catch (ex) {
    print('(1.1)Error occured during division: ${ex.toString()}');
  }
}

// async error handling catches async exceptions
void main2() {
  divideFullAsync(1, 0)
      .then((result) => print('(2) 1 / 0 = $result'))
      .catchError(
          (error) => print('(2) Error occured during division: $error'));
}

// async/await allows to use try/catch for async exceptions
main3() async {
  try {
    await divideFullAsync(1, 0);
    print('3');
  } catch (error) {
    print('(3) Error occured during division: $error');
  }
}

main4() async {
//  try {
//    await divideFullAsyncNested();
//  } on Exception catch (e) {
//    print("(4) ${e.toString().replaceAll('Exception:', '').trimLeft().trimRight()}");
//  }
  try {
    divideFullAsyncNested()
        .then((v) => print(v))
        .catchError((err) => print(Constants.refinedExceptionMessage(err)));
  } on Exception catch (e) {
    print("(4) ${Constants.refinedExceptionMessage(e)}");
  }
}