Flutter/Dart第04天:Dart异步编程(Future和async/await)

打印 上一主题 下一主题

主题 921|帖子 921|积分 2763

Dart官网代码实验室:https://dart.dev/codelabs/async-await
重要说明:本博客基于Dart官网代码实验室,但并不是简单的对官网文章进行翻译,我会根据个人研发经验,在覆盖官网文章核心内容情况下,加入自己的一些扩展问题和问题演示和总结,包括名称解释、使用场景说明、代码样例覆盖、最后完整的场景编程等。
启蒙:错误的异步编程样例

下面是一个错误的异步编程样例,大概过程:通过模拟网络API获取订单ID,然后组织订单ID文案,最终输出问题。
我们期望最终输出的是正确的订单ID文案,可结果并不符合我们的期望:订单ID并不是T2023092900001,而是Instance of 'Future'
  1. // 1.1 创建订单消息
  2. String createOrderMessage() {
  3.   var order = fetchOrderID();
  4.   return '订单ID: $order';
  5. }
  6. // 1.2 获取订单ID内容
  7. Future<String> fetchOrderID() =>
  8.   // 假设获取订单ID是一次网络交互,处理过程需要2秒钟,因此模拟了2秒钟返回订单ID
  9.   Future.delayed(
  10.   const Duration(seconds: 2),
  11.   () => 'T2023092900001',
  12. );
  13. void main() {
  14.   // 1. 启蒙:错误的异步编程样例
  15.   final message = createOrderMessage();
  16.   print(message);
  17.   // 结果:订单ID: Instance of 'Future<String>'
  18. }
复制代码
同步编程和异步编程说明:

  • 同步编程:按照代码块顺序执行代码块,前面代码块没有执行完成之前,后面代码被阻塞
  • 异步编程:异步操作代码块完成初始化之后,后面代码块就可以执行了(非阻塞),异步代码块执行完成(如上面样例等待2秒钟),执行完成回调代码块(即回调)。
Future异步结果说明

异步操作的结果都是Future类的实例(https://api.dart.cn/stable/3.1.3/dart-async/Future-class.html),异步操作有2种状态:未完成完成状态。调用异步代码块(或函数),返回值都是未完成状态的结果。

  • 未完成状态:调用一个异步函数,返回一个Future结果,在异步操作执行结束或者执行出错之前的状态。
  • 完成状态:异步操作执行结束正常返回结果或者执行出错,都是完成状态。正常完成的返回结果即Futrue的T(如:Future),如果无需返回结果,则为void,即异步函数的返回值为Future。如果异步函数执行出错,则返回结果是一个Error,可以进行捕获。
下面2个代码样例,分别为返回值为void和出错结果:
  1. // 2.1 异步操作无返回值
  2. Future<void> fetchOrderID2() {
  3.   // 模拟了2秒钟输出了订单ID
  4.   return Future.delayed(const Duration(seconds: 2), () => print('ID2:T2023092900002'));
  5. }
  6. // 2.2 异常操作返回错误
  7. Future<void> fetchOrderID3() {
  8.   return Future.delayed(const Duration(seconds: 2), () => throw Exception('网络异常'));
  9. }
  10. void main() {
  11.   // 2. Future/async/await异步结果说明
  12.   fetchOrderID2();
  13.   print('2. fetchOrderID2-Future/async/await异步结果说明...');
  14.   // 结果:
  15.   // 2. fetchOrderID2-Future/async/await异步结果说明...
  16.   // ID2:T2023092900002
  17.   
  18.   fetchOrderID3();
  19.   print('2. fetchOrderID3-Future/async/await异步结果说明...');
  20.   // 结果:
  21.   // 2. fetchOrderID3-Future/async/await异步结果说明...
  22.   // Unhandled exception:
  23.   // Exception: 网络异常
  24. }
复制代码
async/await异步操作定义和使用

async定义一个异步操作,而await则是使用一个异步操作的结果。
在应用async和await是,有2点需要遵守的基本规则

  • 如果需要定义一个异步函数,则在函数体之前增加async关键字
  • 只有在异步函数中(即函数体前有async关键字的函数),await关键字才会生效(也就是await必须配合async使用)
定义一个异步函数方法样例(即增加async关键字):
  1. // 1. 同步函数转异步函数:无返回结果
  2. // 1.1 同步函数
  3. void funcVoid() {}
  4. // 1.2 异步返回
  5. Future<void> funcVoid() async {}
  6. // 2. 同步函数转异步函数:有返回结果
  7. // 2.1 同步函数
  8. String funcResult() {}
  9. // 2.2 异常函数
  10. Future<String> funcResult() async {}
复制代码
接下来,我们来重写第1张中,异步函数代码,以使结果符合我们预期:

  • 获取订单ID函数fetchOrderID()前,增加await关键字。
  • 创建订单消息的函数createOrderMessageV2(),增加async关键字变成异步函数,同返回结果由String变成Future异步结果。
  • 同样的,main()函数调用了异步函数,因此也需要增加async关键字。
  1. // 3. async/await异步操作定义和使用
  2. Future<String> createOrderMessageV2() async {
  3.   var order = await fetchOrderID();
  4.   return '订单ID: $order';
  5. }
  6. void main() async {
  7.   // 3. async/await异步操作定义和使用
  8.   final messageV2 = await createOrderMessageV2();
  9.   print('3. async/await异步操作定义和使用');
  10.   print('$messageV2');
  11.   // 结果:
  12.   // 3. async/await异步操作定义和使用
  13.   // 订单ID: T2023092900001
  14. }
复制代码
try/catch异步操作的异常处理

在第2章节中,fetchOrderID3()异步方法会抛出异常,从而中断程序处理。异步操作的异常,我们也可以和同步函数调用一样,通过try-catch的方式进行处理。
下面我们把fetchOrderID3()使用的地方进行改写,捕获异常从而不中断我们的程序:
  1. void main() async {
  2.   // 4. try/catch异步操作的异常处理
  3.   try {
  4.     await fetchOrderID3();
  5.     print('4. try/catch异步操作的异常处理.');
  6.   } catch (e) {
  7.     print('4. try/catch异步操作的异常处理: $e');
  8.   }
  9.   // 结果:4. try/catch异步操作的异常处理: Exception: 网络异常
  10. }
复制代码
场景编程:异步编程的大合唱

应用场景假设:对当前登录的用户打个招呼,同时用户退出登录。因为退出登录操作可被降级,因此退出登录需要捕获所有异常。

  • 获取当前登录的用户名,因为是网络API操作,因此是异步操作。
  • 退出登录时,需要获取当前缓存的用户名,设计到存储操作,因此也是异步操作。
  1. // 5.1 组装用户欢迎语
  2. String makeGreeting(String userName) {
  3.   return '欢迎你 $userName';
  4. }
  5. // 5.2 获取用户名,异步操作
  6. Future<String> fetchUserName() async {
  7.   return Future.delayed(const Duration(seconds: 2), () => 'NTopic.CN');
  8. }
  9. // 5.3 用户登录-打声招呼
  10. Future<String> greeting() async {
  11.   final userName = await fetchUserName();
  12.   return makeGreeting(userName);
  13. }
  14. // 5.4 用户退出-再见
  15. Future<String> goodbye() async {
  16.   final userName = await fetchUserName();
  17.   return '$userName 下次再见!';
  18. }
  19. void main() async {
  20.   // 5. 场景编程:异步编程的大合唱
  21.   print('5. 场景编程:异步编程的大合唱...');
  22.   print(await greeting());
  23.   try {
  24.     print(await goodbye());
  25.   } catch (e) {
  26.     print('5. 场景编程:异步编程的大合唱-Goodbye异常: $e');
  27.   }
  28.   // 结果:
  29.   // 5. 场景编程:异步编程的大合唱...
  30.   // 欢迎你 NTopic.CN
  31.   // NTopic.CN 下次再见!
  32. }
复制代码
最后-完整的实例代码

本文介绍的完整的实例代码:
  1. // 第04天:异步编程
  2. // 1.1 创建订单消息
  3. String createOrderMessage() {
  4.   var order = fetchOrderID();
  5.   return '订单ID: $order';
  6. }
  7. // 1.2 获取订单ID内容
  8. Future<String> fetchOrderID() =>
  9.     // 假设获取订单ID是一次网络交互,处理过程需要2秒钟,因此模拟了2秒钟返回订单ID
  10.     Future.delayed(
  11.       const Duration(seconds: 2),
  12.       () => 'T2023092900001',
  13.     );
  14. // 2.1 异步操作无返回值
  15. Future<void> fetchOrderID2() {
  16.   // 模拟了2秒钟输出了订单ID
  17.   return Future.delayed(const Duration(seconds: 2), () => print('ID2:T2023092900002'));
  18. }
  19. // 2.2 异常操作返回错误
  20. Future<void> fetchOrderID3() {
  21.   return Future.delayed(const Duration(seconds: 2), () => throw Exception('网络异常'));
  22. }
  23. // 3. async/await异步操作定义和使用
  24. Future<String> createOrderMessageV2() async {
  25.   var order = await fetchOrderID();
  26.   return '订单ID: $order';
  27. }
  28. // 5.1 组装用户欢迎语
  29. String makeGreeting(String userName) {
  30.   return '欢迎你 $userName';
  31. }
  32. // 5.2 获取用户名,异步操作
  33. Future<String> fetchUserName() async {
  34.   return Future.delayed(const Duration(seconds: 2), () => 'NTopic.CN');
  35. }
  36. // 5.3 用户登录-打声招呼
  37. Future<String> greeting() async {
  38.   final userName = await fetchUserName();
  39.   return makeGreeting(userName);
  40. }
  41. // 5.4 用户退出-再见
  42. Future<String> goodbye() async {
  43.   final userName = await fetchUserName();
  44.   return '$userName 下次再见!';
  45. }
  46. void main() async {
  47.   // 1. 启蒙:错误的异步编程样例
  48.   final message = createOrderMessage();
  49.   print(message);
  50.   // 2. Future/async/await异步结果说明
  51.   fetchOrderID2();
  52.   print('2. fetchOrderID2-Future/async/await异步结果说明...');
  53.   // fetchOrderID3();
  54.   print('2. fetchOrderID3-Future/async/await异步结果说明...');
  55.   // 3. async/await异步操作定义和使用
  56.   final messageV2 = await createOrderMessageV2();
  57.   print('3. async/await异步操作定义和使用');
  58.   print('$messageV2');
  59.   // 4. try/catch异步操作的异常处理
  60.   try {
  61.     await fetchOrderID3();
  62.     print('4. try/catch异步操作的异常处理.');
  63.   } catch (e) {
  64.     print('4. try/catch异步操作的异常处理: $e');
  65.   }
  66.   // 5. 场景编程:异步编程的大合唱
  67.   print('5. 场景编程:异步编程的大合唱...');
  68.   print(await greeting());
  69.   try {
  70.     print(await goodbye());
  71.   } catch (e) {
  72.     print('5. 场景编程:异步编程的大合唱-Goodbye异常: $e');
  73.   }
  74. }
复制代码
我的本博客原地址:https://ntopic.cn/p/2023092901

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

滴水恩情

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表