【问题标题】:Async function is not returning expected data in Flutter异步函数未在 Flutter 中返回预期数据
【发布时间】:2020-07-07 03:17:36
【问题描述】:

我正在尝试使用 Firebase 验证电话号码。但是从异步函数中得到了意想不到的结果。这是我的代码:

  bool isVerificationSuccess = false;

  Future<bool> verifyUserPhoneNumber(String phoneNumber) async {
    await FirebaseAuth.instance.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      timeout: Duration(seconds: 60),
      verificationCompleted: (credential) => verificationComplete(credential),
      verificationFailed: (authException) => verificationFailed(authException),
      codeAutoRetrievalTimeout: (verificationId) =>
          codeAutoRetrievalTimeout(verificationId),
      codeSent: (verificationId, [code]) => smsCodeSent(verificationId, [code]),
    );
    print("Status from service : $isVerificationSuccess");
    return isVerificationSuccess;
  }

  verificationComplete(AuthCredential credential) async {
    FirebaseUser user = await FirebaseAuth.instance.currentUser();

    user.linkWithCredential(credential).then((_) {
      print("User Successfully Linked");
      isVerificationSuccess = true;
    }).catchError((e) {
      print("Linking Error : ${e.toString()}");
    });
  }

这是输出:

 Status from service : false
 User Successfully Linked

所以这里verifyUserPhoneNumber 函数甚至在FirebaseAuth.instance.verifyPhoneNumber() 完成之前就返回了,所以即使验证成功,它也不会返回预期的数据(总是false)。这里有什么问题?

【问题讨论】:

    标签: flutter dart async-await firebase-authentication


    【解决方案1】:

    问题在于这一行的函数调用:

    verificationCompleted: (credential) => verificationComplete(credential),
    

    不等待,即使函数本身是异步的。那么会发生什么:

    1. verifyUserPhoneNumber 是从某个地方调用的
    2. FirebaseAuth.instance.verifyPhoneNumber 等待中
    3. 在这个调用中,verificationComplete(credential) 被调用,因为它本身包含一个等待语句,它会被 Dart 事件循环排队。所以FirebaseUser user = await FirebaseAuth.instance.currentUser(); 之后发生的任何事情都会被延迟!
    4. verifyUserPhoneNumber 现在继续执行并返回 false
    5. 返回值后,现在轮到verificationComplete 处理。它会改变布尔值,然后退出。

    这里我的建议是不要使用全局变量,而是例如调用verificationComplete FirebaseAuth.instance.verifyPhoneNumber 已经完全处理完毕并返回。

    编辑:这是理论上解决问题的方法,尽管我承认它不是特别漂亮:

      Future<bool> verifyUserPhoneNumber(String phoneNumber) async {
        final completer = Completer<AuthCredential>();
        await FirebaseAuth.instance.verifyPhoneNumber(
          phoneNumber: phoneNumber,
          timeout: Duration(seconds: 60),
          verificationCompleted: completer.complete,
          verificationFailed: completer.completeError,
          codeAutoRetrievalTimeout: (verificationId) =>
              codeAutoRetrievalTimeout(verificationId),
          codeSent: (verificationId, [code]) => smsCodeSent(verificationId, [code]),
        );
        try {
          final credential = await completer.future;
          return await verificationComplete(credential);
        } catch (e) {
          verificationFailed(e);
          return false;
        }
      }
    
      Future<bool> verificationComplete(AuthCredential credential) async {
        FirebaseUser user = await FirebaseAuth.instance.currentUser();
        try {
          await user.linkWithCredential(credential);
          print("User Successfully Linked");
          return true;
        } catch (e) {
          print("Linking Error : ${e.toString()}");
          return false;
        }
      }
    

    【讨论】:

    • 调用验证完成需要凭据。那么在 FirebaseAuth.instance.verifyPhoneNumber 完成后如何获得凭证。整个事情真的很混乱!
    • 我添加了一个你可以做什么的例子。
    • 试过了。但没有变化。
    • 好吧,我可能理解错了。文档对此并不完全清楚,但FirebaseAuth.instance.verifyPhoneNumber 可能会在调用任何回调之前退出。请尝试我的新示例。
    • 徒劳无功,您的解决方案甚至无法完成主要任务(号码验证)。
    【解决方案2】:

    如果你可以将isVerificationSuccess = true; 放入函数verificationComplete(credential) 中,那么你可能会得到true 的值

    【讨论】:

    • 是的,虽然它已经在 verifyComplete() 函数中,但可能需要在 then() 块之外更改值。为此 +1。
    • 但是为什么 verifyUserPhoneNumber() 函数不等待就返回值呢?它在verificationComplete() 完成之前返回值。
    • 我认为它与流有关:如果成功,它还会将用户登录到应用程序并更新 [onAuthStateChanged] 流。
    • 如果你能以某种方式在你的代码中实现 onAuthStateChanged 那么它就可以工作
    • 好的,我试试看。
    【解决方案3】:

    所以这里 verifyUserPhoneNumber 函数甚至在 FirebaseAuth.instance.verifyPhoneNumber() 的完成,所以它不是 即使验证通过,也返回预期数据(始终为假) 成功的。这里有什么问题?

    FirebaseAuth.instance.verifyPhoneNumber 使用方法通道plugins.flutter.io/firebase_auth 并调用本机方法进行身份验证。

    一旦验证完成,它就会使用参数verificationCompleted 中指定的回调。但是,从函数定义来看,FirebaseAuth.instance.verifyPhoneNumber 似乎需要一个类型void Function(AuthCredential)。但是,您提供Future&lt;void&gt; Function(AuthCredential)

    这里有什么问题?

    似乎错误的是期望FirebaseAuth.instance.verifyPhoneNumber 在期望void Function (AuthCredential) 时会等待异步回调Future&lt;void&gt; Function(AuthCredential) 完成。

    所以它只是调用你的verificationComplete(AuthCredential credential) async 实现,它返回一个Future&lt;void&gt;。此时,FirebaseAuth.instance.verifyPhoneNumber 已完成,控制移动到 printreturn isVerificationSuccess,这仍然是 false,因为 verificationComplete(AuthCredential credential) async 返回的未来尚未解决。

    所以,

    1. verifyUserPhoneNumber 返回一个Future&lt;void&gt;
    2. 在您指定isVerificationSuccess = true; 以在成功验证后重新构建您的应用后,使用setState 内的setState 更新应用的状态

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-31
      • 2012-01-21
      • 1970-01-01
      • 2021-04-04
      • 2019-10-03
      • 1970-01-01
      • 2018-10-17
      • 2021-03-05
      相关资源
      最近更新 更多