Dart语言await关键字详解

await 是 Dart 中用来等待异步操作完成并获取结果的关键字。在使用 await 关键字时,需要将其放在一个 async 标记的方法中,以告诉 Dart 编译器这个方法中可能包含 await 表达式。当遇到 await 表达式时,Dart 会暂停当前方法的执行,并等待被 await 的异步操作完成并返回结果,然后再继续执行方法。下面是一个简单的使用 await 的例子:

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return "Hello, World!";
}

void main() async {
  print("Before fetchData.");
  String data = await fetchData();
  print("After fetchData: $data");
}

在这个例子中,我们定义了一个名为 fetchData 的异步方法,它会延迟两秒钟后返回字符串 "Hello, World!"。然后,在 main 方法中,我们先打印一条消息 "Before fetchData.",然后使用 await 关键字等待 fetchData 方法的结果,并将其赋值给变量 data。最后,我们打印一条消息 "After fetchData: $data",并在其中使用了 $data 来输出 fetchData 方法返回的结果。

需要注意的是,await 关键字只能在异步方法中使用,并且异步方法需要使用 async 关键字进行标记。同时,await 后面必须是一个 Future 对象或者一个返回 Future 对象的方法调用。如果在同步方法中使用 await 关键字,或者将 await 关键字用在一个不返回 Future 对象的方法调用上,Dart 编译器将会报错。

另外,当使用 await 关键字时,有时需要处理异步操作可能抛出的异常。通常情况下,我们可以使用 try-catch 块来捕获这些异常。下面是一个使用 await 和 try-catch 的例子:

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  throw Exception("Failed to fetch data.");
}

void main() async {
  try {
    String data = await fetchData();
    print(data);
  } catch (e) {
    print("An error occurred: $e");
  }
}

在这个例子中,我们定义了一个名为 fetchData 的异步方法,它会延迟两秒钟后抛出一个异常。然后,在 main 方法中,我们使用 try-catch 块来处理 fetchData 方法可能抛出的异常。当 fetchData 方法抛出异常时,程序会跳转到 catch 块,并打印一条包含异常信息的错误消息。

在 Dart 中,await 可以用于等待多个异步操作的完成,有两种常用的方法:Future.waitStream.forEach

Future.wait

Future.wait 接受一个 Future 对象列表作为参数,并返回一个 Future 对象,该对象在列表中所有 Future 对象都完成后才会完成。使用 await 等待 Future.wait 的结果可以等待多个异步操作的完成。下面是一个使用 Future.wait 的例子:

Future<int> fetchInt(int n) async {
  await Future.delayed(Duration(seconds: n));
  return n;
}

void main() async {
  var futures = <Future<int>>[
    fetchInt(2),
    fetchInt(1),
    fetchInt(3),
  ];

  var results = await Future.wait(futures);

  print(results); // [2, 1, 3]
}

在这个例子中,我们定义了一个名为 fetchInt 的异步方法,它会延迟指定的秒数后返回一个整数。然后,在 main 方法中,我们创建了一个包含三个 fetchInt 方法返回的 Future 对象的列表 futures。使用 Future.wait 方法等待这三个异步操作的完成,并将它们的结果存储在变量 results 中。最后,我们打印出 results 的值,它应该是一个包含三个整数的列表。

需要注意的是,如果列表中有任何一个 Future 对象抛出了异常,Future.wait 方法会立即停止等待并抛出异常。因此,使用 Future.wait 时需要小心处理可能出现的异常情况。

Stream.forEach

Stream.forEach 方法可以用于对 Stream 中的每个数据进行操作,并且该方法也可以使用 await 等待 Stream 中所有数据的处理完成。下面是一个使用 Stream.forEach 的例子:

Stream<int> countStream(int n) async* {
  for (int i = 0; i < n; i++) {
    yield i;
    await Future.delayed(Duration(seconds: 1));
  }
}

void main() async {
  await countStream(3).forEach((int i) async {
    print(i);
    await Future.delayed(Duration(seconds: 1));
  });
  print("Done");
}

在这个例子中,我们定义了一个名为 countStream 的异步方法,它会生成一个包含指定个数整数的 Stream 对象。然后,在 main 方法中,我们使用 Stream.forEach 方法对 countStream 生成的 Stream 对象中的每个整数进行操作。在操作中,我们使用 await 等待每个整数的处理完成,并使用 await 等待一个延迟 1 秒的异步操作。最后,当所有整数的处理都完成后,我们打印一条消息 "Done"。

需要注意的是,在使用 Stream.forEach 方法时,如果回调函数抛出了异常,整个处理过程会立即停止并抛出异常。

使用 await 进行异步操作的等待时,需要注意以下几个方面:

1.await 只能用在 async 函数内

在 Dart 中,await 只能用在 async 函数内。如果在非 async 函数中使用 await,编译器会提示语法错误。

2.await 只能用在 Future 或 Stream 上

在 Dart 中,await 只能用在返回 Future 或 Stream 的异步操作上。如果尝试在其他类型的对象上使用 await,编译器会提示类型错误。

3.await 可能抛出异常

使用 await 等待异步操作的完成时,需要注意异步操作可能会抛出异常。如果异步操作抛出了异常,使用 await 的函数也会抛出相同的异常。因此,我们需要在使用 await 的函数中使用 try...catch 块或者 Future.catchError 方法来处理异常。

4.await 可能会阻塞程序执行

使用 await 等待异步操作的完成时,需要注意 await 可能会阻塞程序执行。如果 await 的异步操作耗时较长,程序可能会在等待时暂停执行。因此,我们需要谨慎使用 await,尽可能避免在主线程上进行阻塞操作。

5.await 不应该过多嵌套

使用 await 等待异步操作的完成时,需要注意不应该过多嵌套。过多的嵌套会使代码变得难以理解和维护。为了避免嵌套过多,我们可以将异步操作拆分成多个函数,尽可能减少嵌套层数。

6.await 应该避免死锁

使用 await 等待异步操作的完成时,需要注意避免死锁。如果在等待异步操作的过程中阻塞了主线程,可能会导致程序死锁。因此,我们需要避免在主线程上进行耗时的阻塞操作,或者使用 await 的方法应该在后台线程上执行。

powered by Gitbook© 2023 编外计划 | 最后修改: 2023-11-24 03:37:00

results matching ""

    No results matching ""