一、错误记录



编译 Flutter 应用时 , 报如下错误 :

D:\002_Project\001_Flutter\client_terminal>flutter build apk --debug
Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!
Flutter assets will be downloaded from https://storage.flutter-io.cn. Make sure you trust this source!
lib/pages/login_page.dart:38:5: Error: 'await' can only be used in 'async' or 'async*' methods.
    await LoginController().login(context, _accountController.text, _passwordController.text); // 调用登录方法 , 传入账号和密码
    ^^^^^
Target kernel_snapshot_program failed: Exception


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:compileFlutterBuildDebug'.
> Process 'command 'D:\001_Develop\026_Flutter_3.41.7\flutter\bin\flutter.bat'' finished with non-zero exit value 1

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.

BUILD FAILED in 10s
Running Gradle task 'assembleDebug'...                             10.7s
Gradle task assembleDebug failed with exit code 1

D:\002_Project\001_Flutter\client_terminal>

在这里插入图片描述





二、原理分析




1、报错分析


报错问题 :

Error: 'await' can only be used in 'async' or 'async*' methods.

报错原因是 在一个「普通方法 / 按钮点击」里写了 await , 但这个方法 没有加 async 关键字 ;


await 必须配合 async 一起用 , 否则 Dart 直接报错 ;

  • async 是 " 声明这是一个异步方法 " ;
  • await 是 " 等待这个异步任务执行完 " ;
  • await 只能在 async 方法里用 , 否则报错 ;

2、async 和 await 关键字解析


Dart 是单线程语言 , 不能真正阻塞 , 所以用 异步 + 事件循环 实现不卡顿 ;

  • async 关键字 : 把方法包装成异步任务 ;
  • await 关键字 : 暂停当前方法 , 不阻塞主线程 , 等任务完成后再恢复执行 ;

表面看像同步代码 , 底层实际是异步非阻塞执行 ;


async 关键字 作用 : 告诉 Dart 这个方法里面有异步操作 , 不能立刻执行完 ;

  • 加了 async , 方法就变成异步方法
  • 异步方法自动返回 Future
  • 只有加了它 , 里面才能写 await

await 关键字 作用 : 等待后面的异步任务执行完成 , 再继续往下走 ;

  • 等待网络请求
  • 等待文件读写
  • 等待数据库操作
  • 等待延时

3、Dart 单线程


Dart 永远是单线程 , 不会卡住 , 但它有一个机制 :

  • 遇到耗时操作 ( await ) , 就先挂起 , 去干别的事
  • 耗时操作做完 , 再回来继续执行
  • 它不会阻塞线程 , 但可以 " 等待任务结果 " ;

两个写法都是单线程 , 但执行结果完全不一样 ;

  • await doSomething();会等待 , 等里面的异步任务 ( 网络请求 / 延时 ) 真正做完 , 才继续往下走 ;
  • doSomething();不会等待 , 直接立刻往下执行 , 异步任务在后台自己慢慢跑 ;

4、await 调用方式对比


调用下面的方法 :

Future<void> doSomething() async {
  await Future.delayed(Duration(seconds: 2)); // 模拟耗时2秒
  print("任务完成");
}

使用 await 关键字调用

使用

print("开始");
await doSomething(); // 等待2秒
print("结束");

方式 , 进行调用 , 得到的结果 :

开始
 ( 等待2) 
任务完成
结束

不使用 await 关键字调用

使用

print("开始");
doSomething(); // 不等待 , 直接跳过
print("结束");

方式执行 , 结果

开始
结束
 ( 2秒后 ) 
任务完成

5、Future<void> 返回值作用


Future<void> 这个返回值的真正作用 = 让外部能「等待」这个方法执行完 ;

  • 没有它 , 你就不能用 await ;
  • 不能用 await , 你就无法控制执行顺序 ;

Future<void>不是 " 返回内容 " , 而是一个「任务凭证 / 任务小票」 ;

  • void = 没有实际数据返回 ( 不 return 任何值 ) ;
  • Future = 代表一个异步任务 , 可以被等待 ;

await 只能等待 Future 返回值类型的函数 :

  • 你给它 void → 它不认识 , 无法等待 ;
  • 你给它 Future → 它认识 , 可以等待 ;




三、解决方案




1、方案 1 : 方法加上 async


方案 1 : 给方法加上 async ( 最常用、最标准 )

  Future<void> _handleLogin() async { 
    await LoginController().login(username, password);
  }

2、方案 2 : 调用去掉 await


方案 2 : 如果不需要等待 , 直接去掉 await ;

  void _handleLogin() { 
    LoginController().login(username, password);
  }
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐