Dart 之Future

589 阅读4分钟

Dart之Future1.png

前言

什么是Future呢?我看到的第一反应想到的就是翻译——未来,但未来啥呢,没有一个准确的答案。后来通过了解明白Future它表示一个可能还未完成的异步操作的结果,我联系了一下,这不是和未来一个意思吗?我是这样理解的,Dart单线程实现异步操作是通过事件循环机制将异步I/O操作委托给操作系统底层完成,这意味着当前并不知道这个异步操作的结果,需要等待操作系统通知,那这不就是未来的结果吗。是不是感觉很神奇?那下面我们就一起去看看Dart中关于Future的细节吧。

一、Future定义

Future表示一个可能还未完成的异步操作的结果。是不是感觉一脸懵?这可能是我们还没有真正理解异步操作。下面我们以一个生活中的例子来快速回顾一下异步操作。

例子:异步操作就像我们订外卖时骑手送餐这个过程。我们(主线程)不用因为订了外卖(耗时的操作如网络请求)就一直站在门口等待着送达,而是在等待过程中去做其他的事(执行下一个操作),当外卖送达后骑手会通知我们取结果(Future)。

Dart之Future 熊猫送外卖图.png

二、Future的创建

Future的创建就是通过编写代码实现异步操作的过程。通过六大工厂构造函数或异步函数async创建,下面我们逐步介绍。

2.1、Future()

Future(FutureOr<T> computation())需要传入一个返回类型为FutureOr<T>的函数。

  • FutureOr<T>:表示泛型类型的同步结果或泛型类型的异步结果。
  • computation():函数内为用代码实现的异步操作过程。其返回类型为FutureOr<T>意味着可以根据逻辑返回同步结果或异步结果。

下面是Future创建的核心代码。

factory Future(FutureOr<T> computation()) {
  // 1、创建一个未完成的_Future 实例 result。
  _Future<T> result = new _Future<T>();
  // 2、调度异步执行
  Timer.run(() { // 将computation加入事件队列。
    FutureOr<T> computationResult;  // 声明FutureOr<T>类型的变量。
    try {
      // 3、执行传入的异步操作的代码
      computationResult = computation();
    } catch (e, s) {
      // 4、执行过程中出现错误的处理。将错误信息传递给_Future实例
      _completeWithErrorCallback(result, e, s);
      return;
    }
    // 5、结果处理。
    result._complete(computationResult);
  });
  // 6、返回异步操作执行的结果
  return result;
}

示例: 返回一个同步值。

FutureOr<int> buildComputation() {
  int age = 10;
  return age;
}
void main() {
    Future(buildComputation);
}

2.2、Future.delayed()

创建指定延迟时间执行的异步任务。必须传入指定的延迟时间duration。

参数:Duration duration, [FutureOr<T> computation()?]

  • duration:指定的延迟时间。
  • [FutureOr<T> computation()?]:可不传入computation。

示例: 延迟20秒后执行。

 Future.delayed(Duration(seconds: 20),()=>4);

2.3、Future.value()

立即创建一个已完成的Future对象,并携带一个值。

参数:[FutureOr<T>? value]

示例: 携带的值为列表。

Future.value(['Dart',234]);

2.4、Future.error()

立即创建一个已失败的Future对象,并携带指定的错误信息或异常。

参数: Object error, [StackTrace? stackTrace]

  • error: 指定的异常类型。
  • [StackTrace? stackTrace] :错误堆栈信息。

示例: 携带的值为列表。

Future.error(Exception('未满18岁!'),StackTrace.current);

2.5、Future.microtask()

将任务加入微任务队列,让它先执行。

参数:FutureOr<T> computation()

示例: 同步任务 --> 微任务 --> 事件任务

void main() {
    print('同步任务A');
    Future(()=>print('事件任务'));
    Future.microtask(()=>print('微任务'));
    print('同步任务B');
}
输出:
同步任务A
同步任务B
微任务
事件任务

2.6、Future.sync()

将同步代码和异步代码统一包装为Future。

参数:FutureOr<T> computation()

示例:

Future.sync(
    (){
      print('同步代码');
      Future(()=>1);
    });

2.7、async/await语法糖创建

通过语法糖简化异步代码的编写,让编写异步代码和同步代码一样轻松。通过async关键字声明为异步函数,await关键字等待异步操作的完成。

注意: await关键字只能在async声明的异步函数内使用。

示例:

Future<void> dealTask() async {
  await Future.delayed(Duration(seconds: 1)); 
}

三、多个Future的处理

多个Future的处理通过下面四大静态方法实现。

3.1 Future.any()

多个Future中获取最先执行完的那个Future。

示例:

Future.any([
  Future.delayed(Duration(seconds: 100),() => print('A')),
  Future.delayed(Duration(seconds: 80),() => print('B')),
  Future.delayed(Duration(seconds: 40),() => print('C'))
]);

3.2 Future.forEach()

遍历集合中元素,并异步处理。第一个参数为元素集合,第二个为异步函数。

示例:

Future.forEach([1,2,3,4,5], (value) => print(value));

3.3 Future.doWhile()

重复执行异步任务,直到满足终止条件。

示例:

Future.doWhile(()async{
  for(int i in [1,9,3,4,5,6,7,8]){
    if(i%2==0){
      return false;
    }
    print('i:$i');
  };
  return true;
});

3.4 Future.wait()

同时执行多个Future,并等待所有Future执行完后返回一个包含所有Future结果的列表。若其中一个失败则会全部失败(可通过eagerError参数调整)。

参数: Iterable<Future<T>> futures, {bool eagerError = false, void cleanUp(T successValue)?}

  • eagerError: 为true时,当发生第一个错误时终止。
  • cleanUp:任务取消时的清理回调。

示例:

Future.wait([
  Future((){
    print('A');
    throw Exception('A异常');
  }),
  Future((){
    print('B');
    // throw Exception('B异常');
  }),
  Future(()=>print('C'))
],eagerError: false,);

四、异步结果的处理

异步结果分为两种,分别为成功结果和错误结果,分别通过then()方法、catchError()方法处理。

  • then():成功结果的处理。
  • catchError():捕获到错误的处理。
  • whenComplete():无论结果成功或失败都会处理。
  • timeout():超时熔断。
  • onError():类型安全错误处理。
  • asStream():将Future转换为单元素Stream。

示例:

Future.delayed(Duration(microseconds: 5000), (){print('异步代码');
    throw Exception('错误');})
    .then((result) => print('未携带返回结果'))
    .catchError((error) => print('$error'))
    .whenComplete(() => print('清理资源'))
    .timeout(Duration(seconds: 20))
    .onError((e,s){print('$e');});

五、总结

本小节我们从Future的定义出发,首先回顾了异步操作是如何不阻塞主线程的,然后介绍了七种创建Future的方式,其次在了解了单个Future后介绍了四种多个Future的处理,最后介绍了异步结果的处理。下面是本小节的归纳总结:

Future的创建多个Future的处理Future结果的处理
Future()Future.any()then()
Future.delayed()Future.forEach()catchError()
Future.value()Future.doWhile()whenComplete()
Future.error()Future.wait()timeout()
Future.sync()onError()
Future.microtask()asStream()
async/await语法糖