网站地图    收藏   

主页 > 前端 > dart入门教程 >

Dart异步编程: future, async, await

来源:未知    时间:2023-05-09 09:16 作者:小飞侠 阅读:

[导读] Dart异步编程: future, async, await 原文地址: https://dart.cn/codelabs/async-await 这个codelab(代码实验室)教你如何写异步代码使用future和 async 和 await 关键字。使用嵌入式的DartPad编辑器,您可以通...

Dart异步编程: future, async, await

原文地址:https://dart.cn/codelabs/async-await

这个codelab(代码实验室)教你如何写异步代码使用future和async 和 await关键字。使用嵌入式的DartPad编辑器,您可以通过运行示例代码和完成练习来测试您的知识。

为了充分利用此代码实验室,您应该具备以下条件:

该代码实验室涵盖了以下内容:

  • 如何以及何时使用async和await关键字。

  • 使用async和await如何影响执行顺序。

  • 如何使用async函数中的try-catch表达式处理来自异步调用的错误。

完成此代码实验室的估计时间:40-60分钟。


异步代码的重要性

异步操作让您的程序在等待另一个操作完成的同时完成其它工作。下面是一些常见的异步操作:

  • 通过网络获取数据。

  • 写入数据库。

  • 从文件读取数据。

要在Dart中执行异步操作,您可以使用Future类以及async和await关键字。


示例:错误地使用异步函数

以下示例显示了使用异步函数(getUserOrder())的错误方法。 稍后,您将使用async和await修复示例。 在运行此示例之前,请尝试找出问题所在–您认为输出将是什么?


// This example shows how *not* to write asynchronous Dart code.String createOrderMessage () {
  var order = getUserOrder();
  return 'Your order is: $order';}Future<String> getUserOrder() {
  // Imagine that this function is more complex and slow
  return Future.delayed(Duration(seconds: 4), () => 'Large Latte');}void main () {
  print(createOrderMessage());}

下面是为什么这个例子不能打印getUserOrder()最终产生的值:

  • getUserOrder()是一个异步函数,在延迟后,它提供了一个描述用户订单的字符串:“Large Latte”。

  • 为了获得用户的订单,createOrderMessage()应调用getUserOrder()并等待其完成。 由于createOrderMessage()不会等待getUserOrder()完成,因此createOrderMessage()无法获取getUserOrder()最终提供的字符串值。

  • 相反,createOrderMessage()表示待完成的工作:未完成的future。 您将在下一部分中详细了解future。

  • 由于createOrderMessage()无法获取描述用户订单的值,因此该示例无法在控制台上打印“Large Latte”,而是打印“Your order is: Instance of ‘_Future'”。

在接下来的部分中,您将学习有关futures,async, 和 await的知识,以便您能够编写使getUserOrder()打印所需值(“Large Latte”)到控制台的必要代码 。

关键术语:

  • synchronous operation(同步操作): 同步操作会阻止其他操作的执行,直到它完成。

  • synchronous function(同步函数): 同步函数只执行同步操作。

  • asynchronous operation(异步操作): 异步操作一旦启动,就允许其他操作在它完成之前执行。

  • asynchronous function(异步函数): 异步函数至少执行一个异步操作,也可以执行同步操作。


什么是future?

future(小写的“ f”)是Future(大写的“ F”)类的实例。 future表示异步操作的结果,并且可以具有两种状态:未完成或已完成。

备忘: Uncompleted是Dart术语,指的是产生值之前的future的状态。


Uncompleted(未完成)

当你调用一个异步函数时,它会返回一个未完成的future。这个future是等待函数的异步操作完成或抛出一个错误。


Completed(完成)

如果异步操作成功,则future将使用一个值完成。否则就会出现错误。


Completing with a value(用一个值完成)

类型为Future<T>的future以类型为T的值完成。 例如,类型为Future<String>的future会产生一个字符串值。 如果future未产生可用的值,则future的类型为Future<void>。


Completing with an error(带错误完成)

如果该函数执行的异步操作由于某种原因失败,则future将以错误完成。


示例:介绍future

在下面的示例中,getUserOrder()返回打印到控制台后完成的Future。 由于getUserOrder()不会返回可用的值,因此其类型为Future<void>。 在运行示例之前,请尝试预测将首先打印的内容:“Large Latte” 或者 “Fetching user order…”。


Future<void> getUserOrder() {
  // Imagine that this function is fetching user info from another service or database
  return Future.delayed(Duration(seconds: 3), () => print('Large Latte'));}void main() {
  getUserOrder();
  print('Fetching user order...');}

在前面的示例中,即使getUserOrder()在第8行的print()调用之前执行,控制台也会在getUserOrder()的输出(“Large Latte”)之前显示第8行的输出(“Fetching user order…”)。 这是因为getUserOrder()在打印“Large Latte”之前会延迟。


示例:完成时出现错误

运行下面的示例,查看future如何在出现错误时完成。稍后您将学习如何处理该错误。


Future<void> getUserOrder() {// Imagine that this function is fetching user info but encounters a bug
  return Future.delayed(Duration(seconds: 3), () => throw Exception('Logout failed: user ID is invalid'));}void main() {
  getUserOrder();
  print('Fetching user order...');}

在本例中,getUserOrder()结束时将出现一个错误,指示用户ID无效。

您已经了解了futures以及它们是如何完成的,但是如何使用异步函数的结果呢?在下一节中,您将学习如何使用async和await关键字获得结果。

快速回顾一下:

  • 一个Future实例生成一个类型为T的值。

  • 如果future 未产生可用值,则future 的类型为Future<void>。

  • future可能处于以下两种状态之一:未完成或已完成。

  • 当您调用返回future的函数时,该函数会将要完成的工作排队,并返回未完成的future。

  • 当future的操作结束时,该future将带一个值或一个错误完成。

关键术语:

  • Future: Dart的Future类.

  • future: Dart Future类的一个实例。


使用futures:async 并 await

async和await关键字提供了一种声明性的方式来定义异步函数并使用它们的结果。 使用async和await时,请记住以下两个基本准则:

  • 要定义一个异步函数,请在函数体之前添加async:

  • await关键字仅在async函数中起作用。

下面是一个将main()从同步函数转换为异步函数的示例。

首先,在函数体前添加async关键字:


main() async {

注意: 您可能已经注意到一些函数(如上面的main())没有返回类型。这是因为Dart可以为您推断返回类型。 在原型设计时省略返回类型是可以的,但是在编写生产代码时,我们建议您指定返回类型。

如果函数具有声明的返回类型,则将类型更新为Future<T>,其中T是函数返回值的类型。 如果该函数未明确返回值,则返回类型为Future<void>:


Future<void> main() async {

现在你有一个async函数,你可以使用await关键字等待future完成:


print(await createOrderMessage());

如以下两个示例所示,async和await关键字生成的异步代码看起来非常类似于同步代码。


示例:同步函数


// SynchronousString createOrderMessage() {
  var order = getUserOrder();
  return 'Your order is: $order';}Future<String> getUserOrder() {
  // Imagine that this function is
  // more complex and slow.
  return
    Future.delayed(
      Duration(seconds: 4), () => 'Large Latte');}// Synchronousmain() {
  print('Fetching user order...');
  print(createOrderMessage());}// 'Fetching user order...'// 'Your order is: Instance of _Future<String>'


示例:异步函数


// AsynchronousFuture<String> createOrderMessage() async {
  var order = await getUserOrder();
  return 'Your order is: $order';}Future<String> getUserOrder() {
  // Imagine that this function is
  // more complex and slow.
  return
   Future.delayed(
     Duration(seconds: 4), () => 'Large Latte');}// Asynchronousmain() async {
  print('Fetching user order...');
  print(await createOrderMessage());}// 'Fetching user order...'// 'Your order is: Large Latte'

异步的例子在三个方面有所不同:

  • createOrderMessage()的返回类型从String更改为Future<String>。

  • **async**关键字出现在createOrderMessage()和main()的函数体前面。

  • **await**关键字出现在调用异步函数getUserOrder()和createOrderMessage()之前。

关键术语:

  • async: 可以在函数体前使用async关键字将其标记为异步。

  • async function: 一个async函数是一个带有async关键字标签的函数。

  • await: 您可以使用await关键字来获得异步表达式的完整结果。关键字await只在async函数中有效。


具有异步和等待的执行流

一个async函数会同步运行直到第一个await关键字。这意味着在一个async函数体中,在第一个await关键字之前的所有同步代码都会立即执行。

备忘: 在Dart 2.0之前,一个async函数会立即返回,不会在async函数体中执行任何代码。


示例:在异步函数中执行

运行下面的示例,看看在async函数体中执行是如何进行的。你认为输出会是什么?


void printOrderMessage () async {
  print('Awaiting user order...');
  var order = await getUserOrder();
  print('Your order is: $order');}Future<String> getUserOrder() {
  // Imagine that this function is more complex and slow.
  return Future.delayed(Duration(seconds: 4), () => 'Large Latte');}Future<void> main() async {
  countSeconds(4);
  await printOrderMessage();}// You can ignore this function - it's here to visualize delay time in this example.void countSeconds(s) {
  for( var i = 1 ; i <= s; i++ ) {
    Future.delayed(Duration(seconds: i), () => print(i));
  }}

在运行了上述示例中的代码后,尝试颠倒第4行和第5行:


var order = await getUserOrder();print('Awaiting user order...');

注意,输出的时间发生了变化,现在print('Awaiting user order')出现在createOrderMessage()中的第一个await关键字之后。


练习:练习使用async并等待

以下练习是一个失败的单元测试,其中包含部分完成的代码段。 您的任务是通过编写代码使测试通过来完成练习。 您无需实现main()。

要模拟异步操作,请调用为您提供的以下函数:

函数类型签名描述
getRole()Future<String> getRole()获取用户角色的简短描述。
getLoginAmount()Future<int> getLoginAmount()获取用户登录的次数。


第1部分: reportUserRole()

向reportUserRole()函数添加代码,使其执行以下操作:

  • 返回以以下字符串结尾的future:"User role: <user role>"

    • 注意: 您必须使用·getRole()`返回的实际值; 复制并粘贴示例返回值不会使测试通过。

    • 返回值示例:"User role: tester"

  • 通过调用提供的函数getRole()获取用户角色。


第2部分: reportLogins()

实现async函数reportLogins(),以便执行以下操作:

  • 返回字符串 "Total number of logins: <# of logins>"

    • 注意: 您必须使用getLoginAmount()返回的实际值; 复制并粘贴示例返回值不会使测试通过。

    • 来自reportLogins()的示例返回值:"Total number of logins: 57"

  • 通过调用提供的函数getLoginAmount()获取登录次数。


// Part 1//You can call the provided async function fetchRole to return the user roleFuture<String> reportUserRole() async {
  // your implementation here}// Part 2// Implement reportLogins here// You can call the provided async function fetchLoginAmount to return the number of times that the user has logged in.reportLogins(){}

参考调用:


Future<String> reportUserRole() async {
  var username = await fetchRole();
  return 'User role: $username';}Future<String> reportLogins() async {
  var logins = await fetchLoginAmount();
  return 'Total number of logins: $logins';}

注意: 如果您的代码通过了测试,则可以忽略info-level messages.


处理错误

要处理async函数中的错误,使用try-catch:


  try {
    var order = await getUserOrder();
    print('Awaiting user order...');
  } catch (err) {
    print('Caught error: $err');
  }

在async函数中,您可以像在同步代码中一样编写try-catch子句


示例:async并使用try-catch进行await

运行以下示例以查看如何处理异步函数中的错误。 您认为输出结果是什么?


void printOrderMessage () async {
  try {
    var order = await getUserOrder();
    print('Awaiting user order...');
    print(order);
  } catch (err) {
    print('Caught error: $err');
  }}Future<String> getUserOrder() {
  // Imagine that this function is more complex.
  var str = Future.delayed(Duration(seconds: 4), () => throw 'Cannot locate user order');
  return str;}Future<void> main() async {
  await printOrderMessage();}


练习:练习处理错误

下面的练习提供了使用前一节中描述的方法处理异步代码错误的实践。为了模拟异步操作,您的代码将调用以下为您提供的函数:

函数类型签名描述
getNewUsername()Future<String> getNewUsername()返回可用于替换旧用户名的新用户名。

使用async和await来实现一个异步changeUsername()函数,它做以下事情:

  • 调用提供的异步函数getnewusername()并返回其结果。

    • 示例从changeUsername()返回值:"jane_smith_92"

  • 捕获发生的任何错误并返回错误的字符串值。


Future<String> changeUsername () async {
  try {
    return await fetchNewUsername();
  } catch (err) {
    return err.toString();
  }}


练习:将所有内容放在一起

是时候在最后一个练习中练习你所学到的东西了。为了模拟异步操作,本练习提供了异步函数getUsername()'和logoutUser():

函数类型签名描述
getUsername()Future<String> getUsername()返回与当前用户关联的名称。
logoutUser()Future<String> logoutUser()执行当前用户的注销并返回已注销的用户名。

编写以下内容:


第1部分: addHello()

  • 写一个函数addHello(),它接受一个字符串参数。

  • addHello()返回前面带有Hello的字符串。
    例如: addHello('Jon') 返回 'Hello Jon'.


第2部分: greetUser()

  • 编写一个不带参数的函数greetUser()。

  • 要获得用户名,greetUser()调用提供的异步函数getUsername()。

  • greetUser()通过调用addHello()为用户创建一个问候语,传递用户名并返回结果。
    例如: 如果 getUsername() 返回 'Jenny', 那么 greetUser() 返回 'Hello Jenny'.


第3部分: sayGoodbye()

  • 写一个函数saygoodbye()做以下事情:

    • 不接收参数。

    • 捕获任何错误。

    • 调用提供的异步函数logoutUser()。

  • 如果logoutUser()失败,sayGoodbye()返回任何字符串。

  • 如果logoutUser() 成功, sayGoodbye() 返回字符串 '<result> Thanks, see you next time',其中<result>是调用logoutUser()返回的字符串值。


// Part 1addHello(){}// Part 2//You can call the provided async function fetchUsername to return the usernamegreetUser(){}// Part 3//You can call the provided async function logoutUser to logout the usersayGoodbye(){}

参考实现:


String addHello(user) => 'Hello $user';Future<String> greetUser() async {
  var username = await fetchUsername();
  return addHello(username);}Future<String>sayGoodbye() async {
  try {
    var result = await logoutUser();
    return '$result Thanks, see you next time';
  } catch (e) {
    return 'Failed to logout user: $e';
  }}


接下来是什么?

恭喜,您已经完成了代码实验室! 如果您想了解更多信息,请参考以下建议:


最新评论

添加评论

自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论