为啥在 dart/flutter 中不等待 await 返回就执行代码?

Posted

技术标签:

【中文标题】为啥在 dart/flutter 中不等待 await 返回就执行代码?【英文标题】:Why does the code execute without waiting for await to return in dart/flutter?为什么在 dart/flutter 中不等待 await 返回就执行代码? 【发布时间】:2021-06-25 18:18:14 【问题描述】:

我正在开发一个移动应用程序。 我正在使用云功能来完成注册过程。 以下是我的代码的简化可重现示例:- 飞镖文件:-

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:cloud_functions/cloud_functions.dart';

void main() => runApp(MaterialApp(home: Test()));

class Test extends StatefulWidget 
  const Test(
    Key key,
  ) : super(key: key);

  @override
  State<StatefulWidget> createState() => TestState();


class TestState extends State<Test> 
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(
        title: Text('Hi,',style: GoogleFonts.pacifico(fontSize: 25),),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children:<Widget> [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children:<Widget>[
              Padding(padding:const EdgeInsets.all(8)),
              ElevatedButton(
                onPressed:()
                  enroll.call("Password", "email@gmail.com", "name");
                ,
                child:Padding(
                    padding: const EdgeInsets.all(12.0),
                    child: Text('TEST',style: TextStyle(fontSize: 20.0),)
                ),
              ),
            ],
          ),
        ],
      ),
    );
  
  Future<void> enroll(String pass,String email,String name) async 
    HttpsCallable register = FirebaseFunctions.instanceFor(region: 'asia-south1').httpsCallable('Register');
    final funcCall = await register.call(
        <String, dynamic>
          'pass': pass,
          'email': email,
          'Name': name,
        );
    String result = funcCall.data;
    print(result);
  

Firebase 功能代码为:-

import * as functions from "firebase-functions";
const admin = require('firebase-admin');
admin.initializeApp();
exports.Register = functions.region('asia-south1').https.onCall(async (data,context)=>
    const pass = data.pass;
    const Name = data.name;
    const email = data.email;
    await admin.auth()
        .createUser(
            email: email,
            emailVerified: false,
            password: pass,
            displayName: Name,
            disabled: false,
        )
        .then(() => 
            console.log("done")
            return "done"
          )
        .catch((error: any) => 
            console.error(error.code);
            return error.code
        );
                        
);

上次执行期间的 Firebase 控制台日志如下:-

->10:54:37.099 AM
  Register
  Function execution took 662 ms, finished with status code: 200
->10:54:37.095 AM
  Register
  auth/email-already-exists
->10:54:36.438 AM
  Register
  Function execution started

调试日志是:-

I/ViewRootImpl@3af8d1b[MainActivity]( 5889): ViewPostIme pointer 0
I/ViewRootImpl@3af8d1b[MainActivity]( 5889): ViewPostIme pointer 1
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/ConscryptEngineSocket;->setUseSessionTickets(Z)V (greylist-max-q,core-platform-api, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setUseSessionTickets(Z)V (greylist-max-q,core-platform-api, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V (greylist-max-q, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/ConscryptEngineSocket;->setHostname(Ljava/lang/String;)V (greylist-max-q,core-platform-api, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHostname(Ljava/lang/String;)V (greylist-max-q,core-platform-api, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/AbstractConscryptSocket;->setHostname(Ljava/lang/String;)V (greylist-max-q, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V (greylist-max-q,core-platform-api, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([B)V (greylist-max-q, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B (greylist-max-q,core-platform-api, reflection, denied)
W/ckerbin.atchec( 5889): Accessing hidden method Lcom/android/org/conscrypt/AbstractConscryptSocket;->getAlpnSelectedProtocol()[B (greylist-max-q, reflection, denied)
I/flutter ( 5889): null

这两个代码都是实际代码的简化版本,所以请不要说为什么要为此使用云功能..

【问题讨论】:

你能提供一个简化但可重现的例子吗? 好的,我会改写问题 我已经用可重现的代码更改了完整的问题。 您好,您是否在 firebase 函数中尝试过 await admin.auth() 而不是 return admin.auth()?这就是文档设置示例以返回承诺的方式。 firebase.google.com/docs/functions/… 它也无济于事 【参考方案1】:

仔细查看您的 Firebase 函数,我发现了两个问题:

    在同一函数中同时使用 awaitthen 关键字。这两个语句做同样的事情。引用此accepted answer。

await 只是.then() 的内部版本(基本上做同样的事情)。选择其中之一的原因与所需的编码风格或编码便利性有关。当然,解释器有更多的机会来优化事物 在内部使用 await,但您不太可能决定使用哪个。

换句话说,同时使用它们是一种开销。

    下一段代码仅在 then 嵌套 Promise 的上下文中返回结果,这与云函数寄存器的上下文不同。换句话说,您在颤振中收到一个空值,因为云函数在其运行上下文中没有返回任何内容。
.then(() => 
        console.log("done")
        return "done"
      )
.catch((error: any) => 
        console.error(error.code);
        return error.code

正如我之前提到的,thenawait 用于相同目的,因此您有两个选项来更正代码。

你可以使用await:

exports.Register = functions.https.onCall(async (data, context) => 
[...]
 try 
      await admin.auth()
        .createUser(
            email: email,
            emailVerified: false,
            password: pass,
            displayName: Name,
            disabled: false,
        );
        return "user successfully registered";

      
     catch (error) 
      console.log(error);
      return "the function failed;check Cloud Logging for the reason";
    

或者你可以使用then:

exports.RegisterThen = functions.https.onCall((data, context) => 
[...]
return admin.auth()
      .createUser(
            email: email,
            emailVerified: false,
            password: pass,
            displayName: Name,
            disabled: false,
        )
          .then(() => return  "user successfully registered")
          .catch(error => 
            console.log(error);
            return "the function failed;check Cloud Logging for the reason";
          );

请注意,当我使用 await 时,函数是如何用 async 定义的,如果我使用 Promises 中的 then 函数,async 是如何不使用的。

希望对你有用

【讨论】:

成功了!从现在开始我会牢记这一点

以上是关于为啥在 dart/flutter 中不等待 await 返回就执行代码?的主要内容,如果未能解决你的问题,请参考以下文章

Dart / Flutter:错误“流已被收听。” fa“for循环”中的&&“等待”失败

Dart/Flutter:当作为参数传递时,List 中的元素字符串变为空(为啥??)

为啥 build_runner 在 dart/flutter 中序列化 JSON 时不生成文件

好或坏:在 Dart/Flutter 中声明主方法异步

forEach() 循环的 Dart/Flutter 批处理?

如何在Dart / Flutter中使用Flask Server API将图像作为请求发布?