【译】异步编程:Futures

关键点

  • dart是单线程语言
  • 同步代码会阻塞你的程序
  • 使用Future对象来执行异步操做
  • 在async函数里使用await关键字来挂起执行知道一个Future操做完成
  • 或者使用then()方法
  • 在async函数里使用try-catch表达式捕获错误
  • 或者使用catchError()方法
  • 能够使用链式操做future对象来按顺序执行异步函数

dart是一个单线程的编程语言,若是编写了任何阻塞执行线程的代码(例如耗时计算或者I/O),程序就会被阻塞。异步操做可让你在等待一个操做完成的同时完成其余工做。Dart使用Future对象来进行异步操做。html

介绍

先来看一个会致使阻塞的程序代码:web

// Synchronous code
void printDailyNewsDigest() {
  var newsDigest = gatherNewsReports(); // Can take a while.
  print(newsDigest);
}

main() {
  printDailyNewsDigest();
  printWinningLotteryNumbers();
  printWeatherForecast();
  printBaseballScore();
}
复制代码

程序会收集当天的新闻而且打印出来(耗时操做),而后打印一些用户感兴趣的其余信息。编程

<gathered news goes here>
Winning lotto numbers: [23, 63, 87, 26, 2]
Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0 复制代码

这段代码是有问题的,当printDailyNewsDigest()方法作耗时操做阻塞,剩下的其余代码不管多长时间都只有等到printDailyNewsDigest()返回结果以后才能继续执行。api

为了帮助保持应用程序的响应,Dart库的做者在定义可能作耗时工做的函数时用了异步模型。这些函数的返回值是一个future。bash

future是什么

一个future是一个Future的泛型Future<T>对象,表明了一个异步操做产生的T类型的结果。若是结果的值不可用,future的类型会是Future<void>,当返回一个future的函数被调用了,将会发生以下两件事: 1.这个函数加入待完成的队列而且返回一个未完成的Future对象。 2.接着,当这个操做结束了,Future对象返回一个值或者错误。异步

编写返回一个future的代码时,有两面两个可选方法:async

  • 使用async和await关键字
  • 使用FutureAPI

async 和 await

asyncawait 关键字是Dart语言异步支持的一部分。容许你不使用Future api像编写同步代码同样编写异步代码。一个异步函数的函数体前面要生命async关键字,await关键字仅在异步函数里生效。编程语言

下面的代码就是一个使用了Future api的例子。ide

import 'dart:async';

Future<void> printDailyNewsDigest() {
  final future = gatherNewsReports();
  return future.then(print);
  // You don't *have* to return the future here.
  // But if you don't, callers can't await it.
}

main() {
  printDailyNewsDigest();
  printWinningLotteryNumbers();
  printWeatherForecast();
  printBaseballScore();
}

printWinningLotteryNumbers() {
  print('Winning lotto numbers: [23, 63, 87, 26, 2]');
}

printWeatherForecast() {
  print("Tomorrow's forecast: 70F, sunny.");
}

printBaseballScore() {
  print('Baseball score: Red Sox 10, Yankees 0');
}

const news = '<gathered news goes here>';
const oneSecond = Duration(seconds: 1);

// Imagine that this function is more complex and slow. :)
Future<String> gatherNewsReports() =>
    Future.delayed(oneSecond, () => news);
复制代码

main()方法里面printDailyNewsDigest()是一个被调用的,可是由于等待了一秒延迟执行,因此在最后才被打印出来。这个程序的执行顺序以下:函数

1.程序开始执行

2.main方法调用printDailyNewsDigest(),可是不会当即返回,会调用gatherNewsReports()

3.gatherNewsReports()开始收集新闻同时返回一个Future

4.printDailyNewsDigest()使用then()指定一个Future的响应结果,调用then()返回一个新完成的Future结果做为他的回调。

5.剩下的print方法是同步的会依次执行。

6.当全部的新闻都被收集到了,带有新闻信息的Future被gatherNewsReports()返回。

7.then()方法执行,将future返回的String做为参数传递给print打印。

future.then(print)等同于future.then((newsDigest) => print(newsDigest))

因此printDailyNewsDigest()还能够写成:

Future<void> printDailyNewsDigest() {
  final future = gatherNewsReports();
  return future.then((newsDigest) {
    print(newsDigest);
    // Do something else...
  });
}
复制代码

newsDigest是gatherNewsReports()返回的结果。

即时这个Future的类型是Future<void>,也须要传递一个参数给then()的回调,直接用下划线_表示。

Future<void> printDailyNewsDigest() {
  final future = gatherNewsReports();
  return future.then((_) {
    // Code that doesn't use the `_` parameter...
    print('All reports printed.');
  });
}
复制代码

处理错误

在Future api里,能够使用catchError()捕获错误:

Future<void> printDailyNewsDigest() =>
    gatherNewsReports().then(print).catchError(handleError);
复制代码

若是请求不到新闻而且失败了,上面的代码将会执行:

1.gatherNewsReports()完成返回的future携带了error

2.then()方法中的print不会被执行

3.catchError()捕获处处理错误,future被catchError()正常完成并返回,错误不会继续传递下去。

更多细节阅读 Futures and Error Handling

调用多个函数返回futures

有三个函数,expensiveA()expensiveB()expensiveC(), 它们都返回Future对象。你能够顺序调用它们(one by one),或者也能够同时调用三个函数而且当全部结果都返回时再作处理。Future api支持以上的操做。

用then()进行链式调用

按顺序使用then()调用每一个方法

expensiveA()
    .then((aValue) => expensiveB())
    .then((bValue) => expensiveC())
    .then((cValue) => doSomethingWith(cValue));
复制代码

这是个嵌套调用,上一个函数的返回结果做为下一个函数的参数。

使用Future.wait()等待多个方法一块儿完成

若是好几个函数的执行顺序可有可无,能够使用Future.wait()

Future.wait([expensiveA(), expensiveB(), expensiveC()])
    .then((List responses) => chooseBestResponse(responses, moreInfo))
    .catchError(handleError);
复制代码

当传递一个future列表给Future.wait()时,它会当即返回一个Future,可是直到列表里的future都完成的时候,这个Future才会完成,他会返回一个列表里面是每个future产生的结果数据。

若是任何一个调用的函数产生了错误,都会被catchError()捕获到去处理错误。

相关资料