Dart异步编程之Futures[翻译]

  • A+
所属分类:dart
final myFuture = http.get(""https://example.com);
// 许多的Dart异步API都是返回的Futures.

Dart用于异步编程的最基本API之一是futures,Future类型的对象。
在大多数情况下,Dart的Future与其他语言中的Future或Promise API非常相似。

本文将介绍Dart Future背后的概念,并告诉您如何使用Future API。不仅如此,我们还将讨论Flutter FutureBuilder小部件,该小部件可以帮助您根据Future状态来异步更新Flutter UI.

多亏了Dart语言的一些特性(比如async-await),您可能永远不需要直接使用Future API.但是几乎可以肯定,在Dart代码中会遇到Future,您可能想要使用或者阅读使用Future API的代码。

您可以将Future视为提供数据的小礼品盒。有人将这些礼品盒中的一个递给您。过了一会儿,该框弹出,里面有一个值或一个错误。
因此,Future可能处于以下三种状态之一:

  1. Uncompleted: 礼品盒是关闭的
  2. Completed with a value: 礼品盒打开,给你一些礼物(data)
  3. Completed with an error: 礼品盒打开,但是出了一些问题

您将要看到的大多数代码都围绕着处理这三个状态。您将获得Future,您需要确定在打开盒子之前要做什么,您会看到很多1–2–3模式。

Dart异步编程之Futures[翻译]

您可能还记得我们在有关Dart事件循环的文章中提到的的事件循环(如下图所示)。了解Future的好处是,它们实际上只是一个API,旨在简化事件循环的使用。

Dart异步编程之Futures[翻译]

您编写的Dart代码由单个线程执行。在您的应用程序运行的整个过程中,只有一个小线程不断循环运行,从事件队列中提取事件并进行处理。假设您有一些用于下载按钮的代码(以下实现为RaisedButton)。用户点击,您的按钮将开始下载图片。

RaisedButton(
  onPressed: () {
    final myFuture = http.get('https://my.image.url');
    myFuture.then((resp) {
      setImage(resp);
    });
  },
  child: Text('Click me!'),
)

首先发生点击事件。事件循环获取事件,并调用您的点击处理程序(您使用RaisedButton构造函数的onPressed参数进行设置)。
您的处理程序使用http库发出请求(http.get()),并返回给您一个Future(myFuture)。

现在,您有了小盒子(myFuture)。它现在是没有打开的。您要在打开时注册回调,请使用then()

收到礼品盒后,您需要等待,或许还有其他事情发生,用户又做了一些其他操作,而您的小盒子只是放在那里,事件循环一直在进行。

我们等待图像数据下载完成,并且http库说:“太好了!,我们拿到了Future”,然后打开弹出,从而触发您的回调。现在,将交给then()的那段代码执行,您可以在这里进行图像的处理。

在整个过程中,您的代码无需直接接触事件循环。不管发生了什么或发生了什么其他事件,这些都没有关系。您需要做的就是从http库中获取Future,然后在等待Future Completed时要做什么。

在真实代码中,您还要注意处理错误。稍后,我们将向您展示如何执行此操作。

接下来让我们仔细看看Future API,您刚刚看到其中的一些使用。好,第一个问题:如何获取Future的实例?

大多数时候,您不用直接创建Future。这是因为许多常见的异步编程任务已经集成了这个特性。例如,network communication将返回一个Future:

final myFuture = http.get('http://example.com');

获得手机端shared preferences的访问权限也可以返回一个Future:

final myFuture = SharedPreferences.getInstance();

当然,您也可以使用Future构造函数来创建一个Future实例。

Future构造函数

最简单的构造函数是Future(),它接受一个函数并返回与该函数的返回类型匹配的Future。之后,该函数将异步运行,Future执行完毕后返回函数的返回值。
这是使用Future()的示例:

void main() {
  final myFuture = Future(() {
    return 12;
  });
}

我们可以添加一些打印信息使这个异步执行过程变得更清晰。

void main() {
  final myFuture = Future(() {
    print('Creating the future.'); // Prints second.
    return 12;
  });
  print('Done with main().'); // Prints first.
}

运行这个代码,则整个main主体函数将在赋予Future()构造函数的功能之前完成,这是因为Future()构造函数最初只是返回未完成的Future,以下是代码执行的输出结果:

Done with main().
Creating the future.
其他的构造函数

Future.value()如果您在已经知道Future的返回值时,这个会很有用,比如当您在构建使用缓存的服务时:

final myFuture = Future.value(12);

Future.error()使用方法与Future.value()类似,只是带有一个错误对象和一个可选的堆栈追踪。

final myFuture = Future.error(ArgumentError.notNull('input'));

Future.delayed()使用方法与Future()类似,只是它多了一个参数,这个参数用来指定等待的时间长度。比如我们可以在模拟网络请求时来使用它:

final myFuture = Future.delayed(
  const Duration(seconds: 5), //等待5秒
  () => 12,
);

Future的使用

上边我们已经了解了Future的构造,那么接下来让我们谈谈如何使用它,就像我们前边所提到的,使用Future主要要考虑它所处于的三个状态:uncompleted, completed with a value, or completed with an error
下面的代码中我们使用Future.delayed()创建一个Future,该Future在3秒后完成,值为100。

void main() {
  Future.delayed(
    const Duration(seconds: 3),
    () => 100,
  );
  print('Waiting for a value...');
}

如果执行这段代码,main()函数从上到下运行,创建Future并打印“Waiting for a value…”,在整个三秒钟的时间段里,Future状态都是uncompleted。
您可以使用then()为Future注测一个回调,这个回调包含单个参数,该参数类型与Future返回类型相匹配。

void main() {
  Future.delayed(
    const Duration(seconds: 3),
    () => 100,
  ).then((int value) { //100类型是int
    print('The value is $value.'); // Prints later, after 3 seconds.
  });
  print('Waiting for a value...'); // Prints first.
}

这是上边代码段的执行结果:

Waiting for a value... //等待三秒,直到回调执行
The value is 100.

除了注册回调,then()还会返回自己的Future,它可以是任何您想要的返回类型。因此,如果您想要执行多个回调,您可以链接多个then()来注册它。

void main() {
  Future.delayed(const Duration(seconds: 3), () => 22)
    .then((int v) => 'value is $v')
    .then((String v) => print(v));
}

如果您想在发生错误时捕获它并执行另一个回调,您可以使用catchError(),它的使用方式与then()类似,您可以通过链接多个then()catchError()来处理数据或错误。

void main() {
  Future.delayed(
    Duration(seconds: 3),
    () => throw 'Error!', // Complete with an error.
  ).then((value) {
    print(value);
  }).catchError((err) {
    print('Caught $err'); // Handle the error.
  });
  print('Waiting for a value...');
}
weinxin
我的微信
欢迎来撩!!
admin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: