Flutter/Dart第04天:Dart异步编程(Future和async/await)
Dart官网代码实验室:https://dart.dev/codelabs/async-await
重要说明:本博客基于Dart官网代码实验室,但并不是简单的对官网文章进行翻译,我会根据个人研发经验,在覆盖官网文章核心内容情况下,加入自己的一些扩展问题和问题演示和总结,包括名称解释、使用场景说明、代码样例覆盖、最后完整的场景编程等。
启蒙:错误的异步编程样例
下面是一个错误的异步编程样例,大概过程:通过模拟网络API获取订单ID,然后组织订单ID文案,最终输出问题。
我们期望最终输出的是正确的订单ID文案,可结果并不符合我们的期望:订单ID并不是T2023092900001,而是Instance of 'Future<String>'
// 1.1 创建订单消息 String createOrderMessage() { var order = fetchOrderID(); return '订单ID: $order'; } // 1.2 获取订单ID内容 Future<String> fetchOrderID() => // 假设获取订单ID是一次网络交互,处理过程需要2秒钟,因此模拟了2秒钟返回订单ID Future.delayed( const Duration(seconds: 2), () => 'T2023092900001', ); void main() { // 1. 启蒙:错误的异步编程样例 final message = createOrderMessage(); print(message); // 结果:订单ID: Instance of 'Future<String>' }
同步编程和异步编程说明:
- 同步编程:按照代码块顺序执行代码块,前面代码块没有执行完成之前,后面代码被阻塞。
- 异步编程:异步操作代码块完成初始化之后,后面代码块就可以执行了(非阻塞),异步代码块执行完成(如上面样例等待2秒钟),执行完成回调代码块(即回调)。
Future异步结果说明
异步操作的结果都是Future
类的实例(https://api.dart.cn/stable/3.1.3/dart-async/Future-class.html),异步操作有2种状态:未完成和完成状态。调用异步代码块(或函数),返回值都是未完成状态的结果。
- 未完成状态:调用一个异步函数,返回一个
Future<T>
结果,在异步操作执行结束或者执行出错之前的状态。 - 完成状态:异步操作执行结束正常返回结果或者执行出错,都是完成状态。正常完成的返回结果即
Futrue<T>
的T(如:Future<String>
),如果无需返回结果,则为void,即异步函数的返回值为Future<void>
。如果异步函数执行出错,则返回结果是一个Error
,可以进行捕获。
下面2个代码样例,分别为返回值为void和出错结果:
// 2.1 异步操作无返回值 Future<void> fetchOrderID2() { // 模拟了2秒钟输出了订单ID return Future.delayed(const Duration(seconds: 2), () => print('ID2:T2023092900002')); } // 2.2 异常操作返回错误 Future<void> fetchOrderID3() { return Future.delayed(const Duration(seconds: 2), () => throw Exception('网络异常')); } void main() { // 2. Future/async/await异步结果说明 fetchOrderID2(); print('2. fetchOrderID2-Future/async/await异步结果说明...'); // 结果: // 2. fetchOrderID2-Future/async/await异步结果说明... // ID2:T2023092900002 fetchOrderID3(); print('2. fetchOrderID3-Future/async/await异步结果说明...'); // 结果: // 2. fetchOrderID3-Future/async/await异步结果说明... // Unhandled exception: // Exception: 网络异常 }
async/await异步操作定义和使用
async定义一个异步操作,而await则是使用一个异步操作的结果。
在应用async和await是,有2点需要遵守的基本规则:
- 如果需要定义一个异步函数,则在函数体之前增加async关键字
- 只有在异步函数中(即函数体前有async关键字的函数),await关键字才会生效(也就是await必须配合async使用)
定义一个异步函数方法样例(即增加async关键字):
// 1. 同步函数转异步函数:无返回结果 // 1.1 同步函数 void funcVoid() {} // 1.2 异步返回 Future<void> funcVoid() async {} // 2. 同步函数转异步函数:有返回结果 // 2.1 同步函数 String funcResult() {} // 2.2 异常函数 Future<String> funcResult() async {}
接下来,我们来重写第1张中,异步函数代码,以使结果符合我们预期:
- 获取订单ID函数
fetchOrderID()
前,增加await
关键字。 - 创建订单消息的函数
createOrderMessageV2()
,增加async
关键字变成异步函数,同返回结果由String
变成Future<String>
异步结果。 - 同样的,
main()
函数调用了异步函数,因此也需要增加async
关键字。
// 3. async/await异步操作定义和使用 Future<String> createOrderMessageV2() async { var order = await fetchOrderID(); return '订单ID: $order'; } void main() async { // 3. async/await异步操作定义和使用 final messageV2 = await createOrderMessageV2(); print('3. async/await异步操作定义和使用'); print('$messageV2'); // 结果: // 3. async/await异步操作定义和使用 // 订单ID: T2023092900001 }
try/catch异步操作的异常处理
在第2章节中,fetchOrderID3()
异步方法会抛出异常,从而中断程序处理。异步操作的异常,我们也可以和同步函数调用一样,通过try-catch的方式进行处理。
下面我们把fetchOrderID3()
使用的地方进行改写,捕获异常从而不中断我们的程序:
void main() async { // 4. try/catch异步操作的异常处理 try { await fetchOrderID3(); print('4. try/catch异步操作的异常处理.'); } catch (e) { print('4. try/catch异步操作的异常处理: $e'); } // 结果:4. try/catch异步操作的异常处理: Exception: 网络异常 }
场景编程:异步编程的大合唱
应用场景假设:对当前登录的用户打个招呼,同时用户退出登录。因为退出登录操作可被降级,因此退出登录需要捕获所有异常。
- 获取当前登录的用户名,因为是网络API操作,因此是异步操作。
- 退出登录时,需要获取当前缓存的用户名,设计到存储操作,因此也是异步操作。
// 5.1 组装用户欢迎语 String makeGreeting(String userName) { return '欢迎你 $userName'; } // 5.2 获取用户名,异步操作 Future<String> fetchUserName() async { return Future.delayed(const Duration(seconds: 2), () => 'NTopic.CN'); } // 5.3 用户登录-打声招呼 Future<String> greeting() async { final userName = await fetchUserName(); return makeGreeting(userName); } // 5.4 用户退出-再见 Future<String> goodbye() async { final userName = await fetchUserName(); return '$userName 下次再见!'; } void main() async { // 5. 场景编程:异步编程的大合唱 print('5. 场景编程:异步编程的大合唱...'); print(await greeting()); try { print(await goodbye()); } catch (e) { print('5. 场景编程:异步编程的大合唱-Goodbye异常: $e'); } // 结果: // 5. 场景编程:异步编程的大合唱... // 欢迎你 NTopic.CN // NTopic.CN 下次再见! }
最后-完整的实例代码
本文介绍的完整的实例代码:
// 第04天:异步编程 // 1.1 创建订单消息 String createOrderMessage() { var order = fetchOrderID(); return '订单ID: $order'; } // 1.2 获取订单ID内容 Future<String> fetchOrderID() => // 假设获取订单ID是一次网络交互,处理过程需要2秒钟,因此模拟了2秒钟返回订单ID Future.delayed( const Duration(seconds: 2), () => 'T2023092900001', ); // 2.1 异步操作无返回值 Future<void> fetchOrderID2() { // 模拟了2秒钟输出了订单ID return Future.delayed(const Duration(seconds: 2), () => print('ID2:T2023092900002')); } // 2.2 异常操作返回错误 Future<void> fetchOrderID3() { return Future.delayed(const Duration(seconds: 2), () => throw Exception('网络异常')); } // 3. async/await异步操作定义和使用 Future<String> createOrderMessageV2() async { var order = await fetchOrderID(); return '订单ID: $order'; } // 5.1 组装用户欢迎语 String makeGreeting(String userName) { return '欢迎你 $userName'; } // 5.2 获取用户名,异步操作 Future<String> fetchUserName() async { return Future.delayed(const Duration(seconds: 2), () => 'NTopic.CN'); } // 5.3 用户登录-打声招呼 Future<String> greeting() async { final userName = await fetchUserName(); return makeGreeting(userName); } // 5.4 用户退出-再见 Future<String> goodbye() async { final userName = await fetchUserName(); return '$userName 下次再见!'; } void main() async { // 1. 启蒙:错误的异步编程样例 final message = createOrderMessage(); print(message); // 2. Future/async/await异步结果说明 fetchOrderID2(); print('2. fetchOrderID2-Future/async/await异步结果说明...'); // fetchOrderID3(); print('2. fetchOrderID3-Future/async/await异步结果说明...'); // 3. async/await异步操作定义和使用 final messageV2 = await createOrderMessageV2(); print('3. async/await异步操作定义和使用'); print('$messageV2'); // 4. try/catch异步操作的异常处理 try { await fetchOrderID3(); print('4. try/catch异步操作的异常处理.'); } catch (e) { print('4. try/catch异步操作的异常处理: $e'); } // 5. 场景编程:异步编程的大合唱 print('5. 场景编程:异步编程的大合唱...'); print(await greeting()); try { print(await goodbye()); } catch (e) { print('5. 场景编程:异步编程的大合唱-Goodbye异常: $e'); } }
我的本博客原地址:https://ntopic.cn/p/2023092901
本文作者:奔跑的蜗牛,转载请注明原文链接:https://ntopic.cn
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 普通人也能轻松掌握的20个DeepSeek高频提示词(2025版)
· DeepSeek+Zotero