Dart语言async关键字详解

在 Dart 语言中,async 关键字用于指示一个函数是异步函数,该函数可以异步执行代码并返回一个 Future 对象,而不是直接返回结果。

在一个 async 函数中,可以使用 await 关键字来等待一个 Future 对象的结果,当遇到 await 关键字时,执行流会暂停,直到该 Future 对象返回结果或抛出异常。

例如,以下是一个简单的使用 async/await 的示例:

Future<void> printData() async {
  String data = await fetchData();
  print(data);
}

Future<String> fetchData() {
  // 模拟异步操作
  return Future.delayed(Duration(seconds: 2), () => "Hello, World!");
}

void main() {
  printData();
  print("Fetching data...");
}

在上面的代码中,printData 函数使用 async 关键字声明为异步函数,它调用 fetchData 函数并等待它的返回结果。fetchData 函数模拟一个异步操作,返回一个 Future 对象,在 2 秒后返回一个字符串 "Hello, World!"

在 main 函数中,首先调用 printData 函数,它会异步执行并等待 fetchData 函数返回结果,然后打印返回的字符串。同时,main 函数继续执行并打印 "Fetching data..."

在运行上面的代码后,输出结果应该是:

Fetching data...
Hello, World!

可以看到,在 printData 函数中,使用 await 关键字等待 fetchData 函数返回结果时,执行流会暂停,直到 fetchData 函数返回结果,然后才会继续执行下一条语句打印返回的字符串。

async 关键字和 await 关键字是 Dart 语言中用于处理异步操作的两个关键工具,它们可以帮助开发者编写更具可读性和可维护性的异步代码。

除了常规的异步编程模式(如 Future 和 Stream),Dart 的 async/await 还支持一些高级用法,包括以下几种:

1.使用 Completer 和 Future 构建自定义的异步操作。

Completer 是一个非常灵活的工具,它允许我们手动创建 Future 对象,并在需要的时候手动完成这个 Future。通过使用 Completer,我们可以轻松地构建自定义的异步操作,这些操作可以与现有的异步 API 协同工作,或者用于一些特定的场景。下面是一个使用 Completer 构建异步操作的例子:

Future<int> delayedSum(int a, int b) {
  Completer<int> completer = Completer<int>();
  Future.delayed(Duration(seconds: 1), () {
    completer.complete(a + b);
  });
  return completer.future;
}

void main() async {
  int result = await delayedSum(1, 2);
  print(result); // 3
}

2.使用 StreamController 和 Stream 构建异步数据流。

除了使用 Future 之外,Dart 还提供了 Stream 类来表示异步数据流。我们可以使用 StreamController 创建一个新的 Stream,并通过 add 方法向这个 Stream 中添加数据。下面是一个使用 StreamController 构建异步数据流的例子:

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

void main() async {
  Stream<int> stream = countDown(5);
  await for (int i in stream) {
    print(i);
  }
  print('Countdown complete!');
}

在这个例子中,我们使用 async* 关键字来定义一个异步数据流。然后,我们在 for 循环中使用 await for 来等待 Stream 中的数据,并在每次接收到数据时打印它。

3.使用 StreamTransformer 和 StreamSubscription 实现高级的流处理操作。

除了简单的数据流操作之外,Dart 还提供了一些流处理操作,如过滤、映射、组合、合并等等。这些操作可以通过 StreamTransformer 来实现,可以将一个流转换为另一个流,并在转换过程中执行一些自定义的操作。下面是一个使用 StreamTransformer 进行流处理的例子:

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

void main() async {
  Stream<int> stream = countDown(5)
      .where((x) => x % 2 == 0)
      .map((x) => x * 2)
      .take(3);
  await for (int i in stream) {
    print(i);
  }
  print('Countdown complete!');
}

4.使用 StreamGroup 和 StreamZip 对多个流进行操作。

当我们需要同时处理多个异步数据流时,可以使用 StreamGroup 或 StreamZip。StreamGroup 可以将多个流组合成一个新的流,这个新的流中的数据按照原来的流的顺序进行排列。StreamZip 可以将多个流中的数据按照一定的规则进行组合。下面是一个使用 StreamGroup 对多个流进行操作的例子:

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

void main() async {
  StreamGroup<int> group = StreamGroup<int>();
  Stream<int> stream1 = countDown(5);
  Stream<int> stream2 = countDown(3).map((x) => x * 2);
  Stream<int> stream3 = countDown(4).map((x) => x * x);
  group.add(stream1);
  group.add(stream2);
  group.add(stream3);
  Stream<int> stream = group.stream;
  await for (int i in stream) {
    print(i);
  }
  print('Countdown complete!');
}

在这个例子中,我们使用 StreamGroup 将三个异步数据流组合成一个新的流,并按照原来的流的顺序排列数据。然后,我们使用 await for 循环来遍历这个新的流,并在每次接收到数据时打印它。

5.使用 Future.wait() 同时等待多个异步操作完成。

如果我们需要同时等待多个异步操作完成,可以使用 Future.wait() 方法。这个方法接收一个包含多个 Future 对象的列表,并返回一个新的 Future 对象,这个新的 Future 对象在列表中的所有 Future 对象都完成时才会完成。下面是一个使用 Future.wait() 同时等待多个异步操作完成的例子:

Future<int> delayedSum(int a, int b, int delay) async {
  await Future.delayed(Duration(seconds: delay));
  return a + b;
}

void main() async {
  Future<int> future1 = delayedSum(1, 2, 2);
  Future<int> future2 = delayedSum(3, 4, 1);
  Future<int> future3 = delayedSum(5, 6, 3);
  List<int> results = await Future.wait([future1, future2, future3]);
  print(results); // [3, 7, 11]
}

在这个例子中,我们使用 Future.wait() 方法同时等待三个异步操作完成,并在它们都完成时打印它们的结果。

在使用 async/await 进行异步编程时,需要注意以下几点:

  • 异步函数返回的是一个 Future 对象,可以使用 then 方法或者 await 关键字来获取异步操作的结果,否则异步操作将不会被执行。
  • 异步函数内部的代码会被封装在一个微任务中,因此需要注意异步函数中可能会抛出的异常,避免在异步函数内部使用 try-catch 来捕获异常,这样可以确保异常可以被捕获并处理。
  • 在使用 await 关键字等待异步操作完成时,需要注意如果等待的异步操作抛出了异常,则 await 关键字后面的代码将不会被执行,可以使用 try-catch 来捕获这些异常。
  • 如果在同一个异步函数中多次使用 await 关键字等待异步操作完成,这些操作将会按照顺序执行,如果其中一个操作抛出了异常,则后面的操作将不会被执行。
  • 在使用 Future.wait 方法等待多个异步操作完成时,需要注意这些操作是并发执行的,因此可能会影响程序的性能和资源占用,需要根据具体情况选择合适的并发度。
  • 在使用 Stream 进行异步编程时,需要注意 Stream 中的数据可能会在任何时间到达,因此需要使用 StreamSubscription 对象来监听并处理 Stream 中的数据和错误。

异步编程是一个非常强大和灵活的编程技术,可以帮助我们编写更高效和可维护的代码,但是在使用 async/await 进行异步编程时,需要注意以上几点以避免一些常见的错误和问题。

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

results matching ""

    No results matching ""