首页 > 编程语言 >Flutter/Dart第04天:Dart异步编程(Future和async/await)

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

时间:2023-09-29 23:23:51浏览次数:60  
标签:异步 编程 04 await Dart Future print async

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点需要遵守的基本规则

  1. 如果需要定义一个异步函数,则在函数体之前增加async关键字
  2. 只有在异步函数中(即函数体前有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张中,异步函数代码,以使结果符合我们预期:

  1. 获取订单ID函数fetchOrderID()前,增加await关键字。
  2. 创建订单消息的函数createOrderMessageV2(),增加async关键字变成异步函数,同返回结果由String变成Future<String>异步结果。
  3. 同样的,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: 网络异常
}

场景编程:异步编程的大合唱

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

  1. 获取当前登录的用户名,因为是网络API操作,因此是异步操作。
  2. 退出登录时,需要获取当前缓存的用户名,设计到存储操作,因此也是异步操作。
// 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


标签:异步,编程,04,await,Dart,Future,print,async
From: https://www.cnblogs.com/obullxl/p/NTopic2023092901.html

相关文章

  • P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II 题解
    Description给你一个长为\(n\)的排列,\(m\)次询问,每次查询一个区间的逆序对数,强制在线。link\(1\leqn,m\leq10^5\)。Solution考虑分块。首先如果\(l,r\)在同一个块内,可以对于每个块暴力二维前缀和预处理。如果\(l,r\)在不同的块内。设\(bel[l]=x,bel[r]=y\)。首......
  • ubnutu20.04安装playedu
    1curl-fsSLhttps://download.docker.com/linux/ubuntu/gpg|sudoapt-keyadd-2sudoaptupdate3curl-fsSLhttps://download.docker.com/linux/ubuntu/gpg|sudoapt-keyadd-4sudoadd-apt-repository"deb[arch=amd64]https://do......
  • Ubuntu16.04配置ESM软件源修复高危漏洞
    Ubuntu16.04配置ESM软件源修复高危漏洞注:ESM是收费的1、安装ubuntu-advantage-tools和ubuntu-advantage-pro生成pro、ua命令,软件包需要网上下载#dpkg-iubuntu-advantage-tools_28.1_16.04_amd64.deb#dpkg-iubuntu-advantage-pro_28.1_16.04_all.deb  如果有报依赖可执行a......
  • Gym 104270 The 2018 ICPC Asia Qingdao Regional Programming Contest (The 1st Univ
    A.SequenceandSequenceB.KawaExam可以发现,对答案会产生影响的只有割边,把所有边双缩起来,然后就是一个森林。考虑一个树的时候怎么做,就是对于每条边求出这条边两端的众数个数,考虑线段树合并,每次动态维护子树内的众数和子树外的众数。#include<iostream>#include<cstdio>......
  • npm install 报-4048错误
    报错原因:有缓存权限不够 有三种解决方法:第一种:找到.npmrc文件并删除在C:\Users\自己用户的文件夹\下找到.npmrc文件并删除注意:这个文件是隐藏的,需要显示隐藏才能看见第二种方法:直接用命令清理在控制台上输出 npmcacheclean--force 一样可以删除第三种方法:......
  • Setting up development environment with Ubuntu 22.04
    0.Dont'useSnap&Ubuntuappliationstore.90%的问题可以通过重启解决改了IP后需要,禁用网络后再开启才生效 1.Input:https://shurufa.sogou.com/linux/guide 2.IDE:https://www.jetbrains.com/toolbox-app/https://code.visualstudio.com/ 3.RemoteControl:......
  • 【闲暇一写】用Python编写2048游戏(命令行版)
    本篇博文围绕使用Python开发热门游戏2048GAME(命令行版本)代码未做任何优化(原生且随意)、全程以面向过程、MVC的设计思想为主、开发环境是Ubuntu系统下的Pycharm2048是我很久以前学习Python过程中的一个作业,接下来直入正题——一、了解游戏1.介绍《2048》是一款单人在线和移......
  • Flutter/Dart第03天:Dart可迭代集合
    Dart官网代码实验室:https://dart.dev/codelabs/iterables重要说明:本博客基于Dart官网代码实验室,但并不是简单的对官网文章进行翻译,我会根据个人研发经验,在覆盖官网文章核心内容情况下,加入自己的一些扩展问题和问题演示和总结,包括名称解释、使用场景说明、代码样例覆盖等。可迭代......
  • 如何更新ubuntu18.04->20.04->22.04
    https://www.lsjlt.com/news/375465.html#usethiscommandtoupdatethesystemversionfrom18.04to20.04#justfollowthetutorial,andclick'yes'and'keepversion'etc..#thefirstselectionthesystemautopresents#ittakes1h......
  • 【闲暇一写】用Python编写2048游戏(命令行版)
    本篇博文围绕使用Python开发热门游戏2048GAME(命令行版本)代码未做任何优化(原生且随意)、全程以面向过程、MVC的设计思想为主、开发环境是Ubuntu系统下的Pycharm2048是我很久前学习Python过程中的一个作业,直入正题——一、了解游戏1.介绍《2048》是一款单人在线和移动端游戏......