背景

编程中的代码执行,通常分为同步与异步两种。简单说,同步就是按照代码的编写顺序,从上到下依次执行,这也是最简单的我们最常接触的一种形式。但是同步代码的缺点也显而易见,如果其中某一行或几行代码非常耗时,那么就会阻塞,使得后面的代码不能被立刻执行。

异步的出现正是为了解决这种问题,它可以使某部分耗时代码不在当前这条执行线路上立刻执行,那究竟怎么执行呢?最常见的一种方案是使用多线程,也就相当于开辟另一条执行线,然后让耗时代码在另一条执行线上运行,这样两条执行线并列,耗时代码自然也就不能阻塞主执行线上的代码了。

多线程虽然好用,但是在大量并发时,仍然存在两个较大的缺陷,一个是开辟线程比较耗费资源,线程开多了机器吃不消,另一个则是线程的锁问题,多个线程操作共享内存时需要加锁,复杂情况下的锁竞争不仅会降低性能,还可能造成死锁。因此又出现了基于事件的异步模型。简单说就是在某个单线程中存在一个事件循环和一个事件队列,事件循环不断的从事件队列中取出事件来执行,这里的事件就好比是一段代码,每当遇到耗时的事件时,事件循环不会停下来等待结果,它会跳过耗时事件,继续执行其后的事件。当不耗时的事件都完成了,再来查看耗时事件的结果。因此,耗时事件不会阻塞整个事件循环,这让它后面的事件也会有机会得到执行。

我们很容易发现,这种基于事件的异步模型,只适合I/O密集型的耗时操作,因为I/O耗时操作,往往是把时间浪费在等待对方传送数据或者返回结果,因此这种异步模型往往用于网络服务器并发。如果是计算密集型的操作,则应当尽可能利用处理器的多核,实现并行计算。
在这里插入图片描述

于处理异步操作,异步处理成功了就执行成功的操作,异步处理失败就捕获错误或者停止后续操作,一个Future只会对应一个结果,要么成功,要么失败。

Future的所有API的返回值仍然是一个Future对象,所以可以很方便的进行链式调用。

Future.then

模拟延时操作
then中接收异步结果并打印结果

Future.delayed(new Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   print(data);
});

Future.catchError

如果异步任务发生错误,可以在catchError中捕获错误

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里
   print("success");
}).catchError((e){
   //执行失败会走到这里
   print(e);
});

then 接收结果,catchError铺货异常,但并非只有catchError回调才能铺货错误,then方法还有一个可选参数onError,也可以铺货异常

Future.whenComplete

无论异步任务执行成功或失败都需要做一些事时,

  1. 可以分别在 then 或 catchError中关闭以下对话框
  2. 可以使用Future的whenComplete回调
Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里
   print(data);
}).catchError((e){
   //执行失败会走到这里
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

Future.wait

如果需要等待多个异步任务都执行结束后做某些操作,可以使用Future.wait,它接受一个Future数组参数,

  • 只有数组中所有的Future都执行成功后,才会触发then的成功回调,
  • 只要有一个Future执行失败,就会触发错误回调
Future.wait([
  // 2秒后返回结果
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒后返回结果
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

Future.delayed

Future.delay延迟两秒后执行第二个参数里面的内容,返回一个Future对象,执行then后面的内容,then里面方法的参数为delayed第二个参数方法返回的内容,也就是"Hello World!"

Future.delayed(new Duration(seconds: 2),(){
  return "Hello World!";
}).then((data){
  print(data);
});

由于then方法返回还是一个Future,所以可以用链式调用一直拼接then执行相关代码

Future.delayed(new Duration(seconds: 2),(){
  return 'Hello';
}).then((data){
  return data + ' World';
}).then((data){
  return data + ' and';
}).then((data){
  return data + ' 野猿新一!';
}).then((data){
  print(data);
}).catchError((e){
  print(e);
});

运行结果如下

Hello World and 野猿新一!

onError

其实不止catchError方法可以捕获异常,在then方法中还提供了一个可选参数onError,当发生异常的时候,会走到onError参数所传入的方法

Future.delayed(new Duration(seconds: 2), () {
    // 2秒后抛出一个异常
    throw AssertionError("Error");
}).then((data) {
    // 正常执行后回调该方法
    print("success");
}, onError: (e) {
    // 发生异常后回调该方法
    print(e);
});

我们再来看下,若同时设置then方法的onError参数和调用catchError方法,当发生异常时程序会走到哪

Future.delayed(new Duration(seconds: 2), () {
    throw AssertionError("Error");
}).then((data) {
    print("success");
}, onError: (e) {
    print('onError ' + e.toString());
}).catchError((e){
   print('catchError ' + e.toString());
});

输出结果如下

onError Assertion failed

可以看到当同时设置onError和catchError的时候,当发生异常时,程序只会走onError,而不会走到catchError

当然实际写代码的也无需两个都写,这里我比较推荐写catchError,链式调用这样的代码层次比较清晰

Futere <T>

future是一个Futere的一个泛型对象,表示一个异步操作的结果是T类型。如果这个结果不是可以直接使用,可以使用Future。 当调用返回future的函数时,会发生2件事。

函数队列开始执行,结束的时候返回一个Future对象.

当这个操作结束的时候,这个Future对象会返回一个值或者error.

当需要依赖Future去编码时,可以有两个选择

使用asyn和await

使用Future的API

 //视频模块的接口
  Future<Vediomessage> getVedioMessage() async {
    int courseId = Store.read<CourseItemInfo>(context).info.courseId;
    Vediomessage data;
    var response = await FHttp.get('/S9?cou_id=${courseId}');
    var model = Vediomessage.fromJson(response);
    if (model.code == "200") {
      print(200);
      data = model;
    }
    return data;
  }
import 'dart:convert';

class Vediomessage {
  String msg;
  String code;
  List<String> data;

  Vediomessage({this.msg, this.code, this.data});

  Vediomessage.fromMap(Map<String, dynamic> json) {
    msg = json['msg'];
    code = json['code'];
    data = json['data'].cast<String>();
  }

  Map<String, dynamic> toMap() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['msg'] = this.msg;
    data['code'] = this.code;
    data['data'] = this.data;
    return data;
  }
  String toJson() =>
      json.encode(toMap()); //将一个json格式的string 转化成一个Map<String,dynamic>类型的Map

  static Vediomessage fromJson(source) =>
      Vediomessage.fromMap(new Map<String, dynamic>.from(source));
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐