颤振异步等待未按预期工作

Posted

技术标签:

【中文标题】颤振异步等待未按预期工作【英文标题】:Flutter async await not working as expected 【发布时间】:2022-01-22 08:11:29 【问题描述】:

自从我开始使用 Flutter 以来,我面临着一个与 Flutter async-await 相关的问题。大多数时候,我尝试使用 Future 并等待结果,它会跳过等待并获得最短的返回方式。 if i try to print after await the null value prints first and then await is called

here is my onPressed

 onPressed: () async 
                  if (_textEditingController.text.isNotEmpty) 
                    Map a = await Authentication.sendOtp(
                        phoneNum: _textEditingController.text);
                    print(a);
                  
                ,

and my Authentication class:

class Authentication 
 static Future<Map> sendOtp(required String phoneNum) async 
    String? vid;
    try 
      if (!kIsWeb) 
        await FirebaseAuth.instance.verifyPhoneNumber(
          phoneNumber: phoneNum,
          verificationCompleted: (PhoneAuthCredential credential) ,
          verificationFailed: (FirebaseAuthException e) ,
          timeout: const Duration(seconds: 5),
          codeSent: (String verificationId, int? resendToken) 
            print('Code Sent $verificationId');
            vid = verificationId;
          ,
          codeAutoRetrievalTimeout: (String verificationId) ,
        );
       else 
        final recaptchaVerifier = RecaptchaVerifier(
            container: null,
            size: RecaptchaVerifierSize.compact,
            theme: ThemeMode.system as RecaptchaVerifierTheme);
        await FirebaseAuth.instance
            .signInWithPhoneNumber(phoneNum, recaptchaVerifier)
            .then((confirmationResult) 
          vid = confirmationResult.verificationId;
        );
      
      return 'msg': vid, 'val': false;
     on FirebaseAuthException catch (e) 
      print('------$e.code');
      return 'msg': e.code, 'val': true;
     catch (e) 
      print(e);
      return 'msg': null, 'val': true;
    
  

output i get:

I/flutter (14230): msg: null, val: false
E/zzf     (14230): Problem retrieving SafetyNet Token: 7: 
W/System  (14230): Ignoring header X-Firebase-Locale because its value was null.
W/System  (14230): A resource failed to call end. 
W/System  (14230): A resource failed to call end. 
D/EGL_emulation(14230): eglCreateContext: 0xef618f80: maj 2 min 0 rcv 2
E/zzf     (14230): Failed to get reCAPTCHA token with error [The web operation was canceled by the user.]- calling backend without app verification
I/FirebaseAuth(14230): [FirebaseAuth:] Preparing to create service connection to fallback implementation
W/System  (14230): Ignoring header X-Firebase-Locale because its value was null.
I/flutter (14230): Code Sent AJOnW4ROl1S4AeDErwZgls2LAxaQuwURrzDMJ1WNjQH8hWce-BTUeUE21JyCvHpMvfxT4TA8Hcp-mSWFqlzzX-IEd7X6z8ry1mkeCHC7u_ir-lnBL89OP0M6-4kU7BlOKcMPBY5OT4pmpdjETCoyAhrdc8TBR8yJqw
W/FirebaseAuth(14230): [SmsRetrieverHelper] Timed out waiting for SMS.

请帮助更好地理解flutter async-await,或者告诉我哪里做错了,以便我改进我的代码

【问题讨论】:

你想让print('Code Sent $verificationId');先被执行吗? 是的,我希望它等待返回值,然后打印地图的值 除了您得到的出色答案...请不要将async/await.then() 结合使用。这是一个或另一个。技术上可以将两者结合起来,就像完全有可能让你的车被马牵引一样,但这没有任何意义。如果你使用 await,那么 use await. 【参考方案1】:

你没有使用 await 本身错误,但你有错误的期望。

FirebaseAuth.instance.verifyPhoneNumber 将在函数执行后完成它的未来,但它不会等到发送 SMS。此处的 Future 表示电话验证过程已经开始。换句话说,codeSent 回调将在 Future 完成后的稍后时间调用(即直到 SMS 发送给用户):

 /// [codeSent] Triggered when an SMS has been sent to the users phone, and
 ///   will include a [verificationId] and [forceResendingToken].

您需要在您的应用/小部件中考虑该行为。

这是一种方法:

将您的函数定义更改为:

static Future<void> sendOtp(required String phoneNum, required PhoneCodeSent codeSent) 
    String? vid;
    try 
      if (!kIsWeb) 
        await FirebaseAuth.instance.verifyPhoneNumber(
          phoneNumber: phoneNum,
          verificationCompleted: (PhoneAuthCredential credential) ,
          verificationFailed: (FirebaseAuthException e) ,
          timeout: const Duration(seconds: 5),
          codeSent: codeSent, // <~~ passed from your app
          codeAutoRetrievalTimeout: (String verificationId) ,
        );
      
    // the rest is the same without return values tho

由于您编辑了上面的代码以让应用在调用 codeSent 后接收数据,因此您无需从 sendOtp 返回任何内容。

现在在您的小部件中:

onPressed:  () async 
    if (_textEditingController.text.isNotEmpty) 
      await Authentication.sendOtp(
        phoneNum: _textEditingController.text,
        codeSent: (String verificationId, int? resendToken) 
          // #2 Once this is called (which will be after the `print(a)` below),
          // update your app state based on the result (failed or succeeded)
        
      );
      // #1 update your app state to indicate that the 'Message is on the way'
      // maybe show a progress indicator or a count down timer for resending
     // print(a);  <~~ there's no `a` anymore
    
  ;

正如您在上面看到的,代码 #1 将在代码 #2 之前执行,因为稍后会调用 codeSent。我不确定是否有超时,或者您是否必须保留自己的计时器。


如果你不想在 UI 上处理数据,你可以把回调改成别的,让它像以前一样返回 Map:

static Future<void> sendOtp(required String phoneNum, required ValueChanged<Map<String, dynamic>> onCodeSent) 
    String? vid;
    try 
      if (!kIsWeb) 
        await FirebaseAuth.instance.verifyPhoneNumber(
          phoneNumber: phoneNum,
          verificationCompleted: (PhoneAuthCredential credential) ,
          verificationFailed: (FirebaseAuthException e) ,
          timeout: const Duration(seconds: 5),
          codeSent: (String verificationId, int? resendToken) 
            onCodeSent.call('msg': verificationId, 'val': true);
          ,
          codeAutoRetrievalTimeout: (String verificationId) ,
        );
      
// the rest is the same without return values tho

在您的小部件上,您可以执行以下操作:

onPressed:  () async 
    if (_textEditingController.text.isNotEmpty) 
      await Authentication.sendOtp(
        phoneNum: _textEditingController.text,
        codeSent: (Map<String, dynamic> map) 
          setState(() 
             a = map
          );
        
      );
 
    
  ;

同样适用于网页部分,只需使用地图调用回调:

final recaptchaVerifier = RecaptchaVerifier(
            container: null,
            size: RecaptchaVerifierSize.compact,
            theme: ThemeMode.system as RecaptchaVerifierTheme);
        await FirebaseAuth.instance
            .signInWithPhoneNumber(phoneNum, recaptchaVerifier)
            .then((confirmationResult) 
          onCodeSent.call('vid': confirmationResult.verificationId, 'val': true);
        );
      

【讨论】:

谢谢你,工作就像一个魅力,还知道了这个新事物:ValueChanged

以上是关于颤振异步等待未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

带有导航器的颤振模态底部工作表未按预期弹出

react-redux s-s-r 异步 api 调用未按预期工作

Rx.combineLatest2 未按预期工作

WebDriver 隐式等待未按预期工作

使用 discordJDA 等待消息未按预期工作

Mongoose pre.save() 异步中间件未按预期工作