Dart基础(九)-异步支持

2022-01-12  本文已影响0人  苍眸之宝宝

1.简介:

  Dart库充满了返回Future或Stream对象的函数;这些函数都是异步的。这些函数在做一些可能耗时的操作(如I/O)时,会立即返回一个Future或Stream对象,进行函数后面代码的执行,而不需要等待该操作完成后再执行函数后面的代码。
  Dart中使用async``await关键字来实现异步编程,但是和Java、OC等异步不同,代码类似于同步代码,实际上是异步执行的。

关键术语:
同步操作:同步操作阻止其他操作执行,直到它完成。
同步函数:同步函数只执行同步操作。
异步操作:异步操作一旦启动,就允许其他操作在它完成之前执行。
异步函数:一个异步函数至少执行一个异步操作,也可以执行同步操作。

异步场景:

2.处理Future

  Future表示一个不会立即完成的计算过程。与普通函数直接返回结果不同的是异步函数返回一个将会包含结果的Future。该 Future会在结果准备好时通知调用者。
future与Future:
  future(小写“f”)是Future类的一个实例。future表示异步操作的结果,可以有两种状态:未完成uncompleted或已完成completed
未完成uncompleted
  当调用一个异步函数时,它将返回一个未完成的future。该future表示函数正在等待函数的异步操作完成或抛出错误。
成功的完成Completing with a value
  Future <T>类型的future以T类型的值结束。例如,Future <String>类型的future产生一个字符串值。如果future没有产生一个可用的值,那么future的类型是Future <void>
错误的完成Completing with an error
  如果函数执行的异步操作由于任何原因失败,则future函数将以错误的方式完成。

// 引入Future,并返回一个Future<void>的例子
Future<void> fetchUserOrder() {
  // Imagine that this function is fetching user info from another service or database.
  return Future.delayed(const Duration(seconds: 2), () => print('Large Latte'));
}

// 返回一个future错误的例子
Future<void> fetchUserOrder() {
// Imagine that this function is fetching user info but encounters a bug
  return Future.delayed(const Duration(seconds: 2),
      () => throw Exception('Logout failed: user ID is invalid'));
}

Futuer和future总结:

3.异步async和等待await

  asyncawait关键字提供了一种声明性的方式来定义异步函数并使用它们的结果;使用的基本原则:

Future<String> createOrderMessage() async {
  print("message star");
  var order = await fetchUserOrder3();
  print("message return");
  return 'Your order is: $order';
}

Future<String> fetchUserOrder3() async {
  // Imagine that this function is more complex and slow.
  print("order star");
  var result = await Future.delayed(
    const Duration(seconds: 2), () {
    return 'Large Latte';
  });
  print("order return");
  return result;
}

// 同步函数中调用
print('message top');
createOrderMessage().then((value) {
  print(value);
});
print('message bottom');

// 打印顺序
flutter: message top
flutter: message star
flutter: order star
flutter: message bottom
flutter: order return
flutter: message return
flutter: Your order is: Large Latte

  如何解读上述代码的打印顺序呢?

  1. 同步函数首先打印:message top;
  2. 同步调用异步函数createOrderMessage,进入该函数;
  3. 在函数createOrderMessage中,首先同步打印:message star;
  4. 同步调用await fetchUserOrder3(); ,进入函数fetchUserOrder3;
  5. 在函数fetchUserOrder3中,首先同步打印:order star;
  6. 同步调用await Future.delayed ,进行函数Future.delayed调用;
  7. 函数Future.delayed延迟执行,为异步调用,直接返回一个Future<String>的实例future,并加入异步队列中等待执行;
  8. 函数fetchUserOrder3检测到异步执行,直接返回一个future,退出同步执行,将异步部分加入异步队列中等待执行;
  9. 函数createOrderMessage与6相同;
  10. 同步函数中异步函数createOrderMessage的同步部分执行完毕,继续执行下面同步代码,打印:message bottom;
  11. 队列要求先进先出,当最先加入异步队列的任务已经完成后,会以加入的次序执行后续加入的异步代码,后面的打印书序为:
    order return
    message return
    Your order is: Large Latte

3.异步错误error处理:

  在异步函数中,如同同步函数一样使用try-catch处理出现的错误或者异常。

Future<void> printOrderMessage() async {
  try {
    print('Awaiting user order...');
    var order = await fetchUserOrder();
    print(order);
  } catch (err) {
    print('Caught error: $err');
  }
}

Future<String> fetchUserOrder() {
  // Imagine that this function is more complex.
  var str = Future.delayed(
      const Duration(seconds: 4),
      () => throw 'Cannot locate user order');
  return str;
}

Future<void> main() async {
  // 会抛出Cannot locate user order异常
  await printOrderMessage();
}

4.处理流Streams

官方文档
  Stream 是一系列异步事件的序列。其类似于一个异步的 Iterable,不同的是当你向 Iterable 获取下一个事件时它会立即给你,但是 Stream 则不会立即给你而是在它准备好时告诉你。
  Stream概要:

4.1接收Stream事件:

  Stream可以通过许多方式创建,而这些所有的创建方式都可以相同的方式在代码中使用:像使用for循环 迭代一个Iterable一样,我们可以使用 异步for循环 (通常我们直接称之为await for)来迭代Stream中的事件。例如:

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

  上面代码只是简单地接收整型事件流中的每一个事件并将它们相加,然后返回(被Future包裹)相加后的整型值。当循环体结束时,函数会暂停直到下一个事件到达或Stream完成。内部使用await for循环的函数需要使用async关键字标记。
下面的示例中使用了async*函数生成一个简单的整型Stream来测试上一个代码片段:

Future<int> sumStream(Stream<int> stream) async {
  var sum = 0;
  await for (final value in stream) {
    sum += value;
  }
  return sum;
}

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    yield i;
  }
}

// 调用
var stream = countStream(10);
var sum = sumStream(stream);
sum.then((value) {
  print(value);
});
4.2Stream错误处理:

  当Stream再也没有需要处理的事件时会变为完成状态。与此同时,调用者可以像接收到新事件回调那样接收Stream完成的事件回调。当使用await for循环读取事件时,循环会在Stream完成时停止。

  有时在Stream完成前会出现错误;比如从远程服务器获取文件时出现网络请求失败,或者创建事件时出现 bug;在出现错误时,需要将错误告知使用者。

  Stream可以像提供数据事件那样提供错误事件。大多数Stream会在第一次错误出现后停止,但其也可以提供多次错误并可以在在出现错误后继续提供数据事件。在本篇文档中我们只讨论Stream最多出现并提供一次错误事件的情况。

  当使用await for读取Stream时,如果出现错误,则由循环语句抛出,同时循环结束。你可以使用try-catch语句捕获错误。下面的示例会在循环迭代到参数值等于 4 时抛出一个错误:

Stream<int> countStream(int to) async* {
  for (int i = 1; i <= to; i++) {
    if (i == 4) {
      throw Exception('Intentional exception');
    } else {
      yield i;
    }
  }
}

5.隔离区Isolates

  大多数计算机中,甚至在移动平台上,都在使用多核 CPU。为了有效利用多核性能,开发者一般使用共享内存的方式让线程并发地运行。然而,多线程共享数据通常会导致很多潜在的问题,并导致代码运行出错。

  为了解决多线程带来的并发问题,Dart 使用isolate替代线程,所有的 Dart 代码均运行在一个 isolate 中。每一个isolate有它自己的堆内存以确保其状态不被其它 isolate 访问。

  所有的 Dart 代码都是在一个isolate中运行,而非线程。每个 isolate 都有一个单独的执行线程,并且不与其他的isolate共享任何可变对象。

你可以查阅下面的文档获取更多相关信息:

上一篇 下一篇

猜你喜欢

热点阅读