Skip to content

Future

Future是Dart中一个很重要的概念,Future是一个异步操作,它代表一个异步任务的执行结果。Future 不是多线程的。它是一种异步编程的机制,用于处理非阻塞式的计算任务,并在任务完成后返回结果。Future使用单线程执行任务,并通过事件循环(event loop)模型来实现异步操作。
Flutter是单线程UI框架,所以不要把Future线程混淆。Future是异步操作,它代表一个异步任务的执行结果。在实际的Flutter应用中,可以时逻辑从某种程度上避免和UI渲染同时操作,比如渲染时同时出发请求,但是过多的异步操作同样会导致性能问题使得整个UI线程阻塞,所以要同时避免过多的异步操作。
虽然 Future 不是多线程的,但可以使用 async 和 await 关键字来编写更简洁和易读的异步代码。通过使用 async 关键字标记一个函数为异步函数,并在需要等待异步任务结果的地方使用 await 关键字,可以让代码看起来像是同步执行的。

多线程与Future

前面我们说到过,Future只是线程内异步操作的实现,并不属于多线程概念。而且由于Dart是单线程的,因此无法利用多核处理器来并行执行多个任务。然而,Dart 提供了 Isolate(隔离区)机制,它是 Dart 中实现并发的一种方式。通过使用 Isolate,可以在多个隔离区中并行执行多个任务,从而提高应用程序的性能。Isolate 之间通过消息传递进行通信,以确保线程安全。值得注意注意Dart多线程,Isolate之间并不共享内存。在后续的文章内,我们会提到多线程概念。

Future与Promise的差异

Future 和 Promise 是两种不同编程语言中用于处理异步操作的机制。理解起来,Future与Promise十分类似。但返回结果的方式不同:Future 在执行异步操作后,会返回一个 Future 对象,表示将来会返回任务的结果。在代码中,我们可以使用 await 关键字等待 Future 的结果。而 Promise 在执行异步操作后,会返回一个 Promise 对象,我们可以使用 .then() 方法来处理异步操作结束后的结果。
Future 是 Dart 中使用的异步编程的机制,主要依赖于 async/await 关键字来编写异步代码。这种方式使代码看起来像是同步执行的。而 Promise 是在 JavaScript 中使用的异步编程机制,主要依赖于 .then() 和 .catch() 方法来处理异步操作。

Future的使用方法

最常见的,就是申明一个异步函数。

dart
Future<String> fetchData() async {
  return Future.value('Hello, World!');
}

final a = await fetchData(); /// Hello, World!
final a = fetchData(); /// Future<String>

但是我们上面提到过Future 在执行异步操作后,会返回一个 Future 对象,利用这个特性,我们还可以这样使用Future

dart
Future((){
  print('Hello, World!');
});

这样,直接异步执行一个函数,这样做的场景在组件中,其实非常实用,因为很多时候StatefulWidget的initState方法中,调用异步方法,可以避免组件初始化时,造成掉帧。

dart
@override
void initState() {
  super.initState();
  Future(()async{
    await doSomething();
  })
}

通过使用 Future.wait() 方法,可以同时等待多个异步任务的完成,并进行并行处理。

dart
void main() async {
  Future<String> task1 = fetchData1();
  Future<int> task2 = fetchData2();

  List<dynamic> results = await Future.wait([task1, task2]);
  String data = results[0];
  int number = results[1];

  print('Data fetched successfully: $data');
  print('Number fetched successfully: $number');
}

错误处理

在 Future 中,可以使用 try/catch 块来捕获异步操作中的错误。

dart
void main() async {
  try {
    String data = await fetchData();
    print('Data fetched successfully: $data');
  } catch (error) {
    print('Error occurred: $error');
  }
}

语法误区

首先如果一个异步函数中,没有定义返回数据类型为Future<T> 那么返回值不是一个Future,则不能使用await,比如下面的代码中是有差异的。

dart
Future<String> fetchData() async {
  return Future.value('Hello, World!');
}

void fetchDataB() async {
  return Future.value('Hello, World!');
}

final a = await fetchData(); /// Hello, World!
final a = await fetchDataB(); /// 由于返回值不是Future,所以await会报错

上述代码中,你可能注意到我们返回时,使用了 Future.value('Hello, World!'),在dartpad.dev中,你可以运行一下一下的代码试试结果:

dart
Future<String> fetchData() async {
  return 'Hello, World!';
}

提前终止

在Dart中,Future 实例是无法主动终止的,因为它代表的是一个异步操作的结果。一旦 Future 开始执行,它会一直执行直到完成。然而,你可以通过一些技巧来实现在某些条件下“终止”一个 Future 的效果。
使用 Completer:Completer 是一个用于手动完成一个 Future 的类。你可以创建一个 Completer 实例,并在某些条件下调用 complete 方法来完成 Future 对象。

dart
import 'dart:async';

void main() {
  Completer<void> completer = Completer<void>();

  Future<void> myFuture = completer.future;

  // 在某个条件下,调用 complete 方法来完成 Future
  if (condition) {
    completer.complete();
  } else {
    completer.completeError('Terminated');
  }

  myFuture.then((value) {
    print('Future completed successfully');
  }).catchError((error) {
    print('Future terminated with error: $error');
  });
}

通过控制 Completer 的完成或错误完成,可以模拟 “终止” 一个 Future。使用内部标志:在 Future 执行过程中,可以使用一个内部的标志来控制是否继续执行。在每个异步操作之前,检查这个标志并决定是否继续执行。

dart
bool shouldContinue = true;

Future<void> myFuture() async {
  while (shouldContinue) {
    // 执行异步操作
    await doSomethingAsync();
  }
}

通过修改 shouldContinue 标志,可以控制 Future 是否继续执行,并在必要时“终止” 它。 这些方法只能在某个条件下模拟“终止”一个 Future,而不是真正地终止它。在实际情况中,Future 执行过程中的异步操作可能无法立即中断,所以上述方法只是在满足某些条件时提前结束 Future 的执行。不要因为这个示例,就按照这样的方法去写一个复杂的终止网络请求的逻辑。实际上请求类一般都提供了CancelToken,可以取消请求。这个示例,目的是为了让我们掌握如何在Future执行中,减少不必要的运算。

仅用于培训和测试,通过使用本站代码内容随之而来的风险与本站无关。版权所有,未经授权请勿转载,保留一切权利。
ICP备案号:滇ICP备15009214号-13   公安网备:滇公网安备 53312302000061号