likes
comments
collection
share

[Flutter]Dart Future详解

作者站长头像
站长
· 阅读数 14

在客户端的很多操作需要异步执行,使用callback处理异步,很容易写出回调地狱(Callback hell)代码,导致难以维护。所以在Web端出现了Promise,Android有RxJava,可以使用这些工具,将横向嵌套的callback代码改为纵向扩展,更符合人类的直觉,也更加容易维护。配合async,await更是能以同步的方式写异步代码。

Dart也同样提供了处理异步的工具Future,和JavaScript中的Promise十分相似,有Promise使用经验的人使用Future时会感到十分熟悉。

An object representing a delayed computation.

Future是一个表示延迟计算的对象。代表一些计算将异步进行,并在将来获取到计算的结果。 Future有两种状态:PendingCompletePending表示任务正在执行中,还未获取到结果;Complete表示任务已经执行完,并且有结果。 结果有两种值:成功(succeed)或者失败(failed), 我们可以使用回调函数来获取结果值。

事件循环

和其他客户端开发框架类似(JS中的EventLoop, Android中的Looper,iOS中的RunLoop),Dart中也有一个无线循环的事件循环,包含两个队列: microtask队列event队列。Dart会不断地循环访问这两个队列,从中取出任务来执行。microtask队列的优先级高于event队列,如果microtask队列不为空,就从中取出任务执行。当microtask队列为空时,才会访问event队列

[Flutter]Dart Future详解

通过scheduleMicrotask方法可以向microtask队列中添加任务,而UI事件、Timer等操作和Future的默认构造方法都是添加到event队列中。

默认构造方法

Future构造方法

通过Future的默认构造方法可以创建一个Future对象,。默认构造方法的签名如下

factory Future(FutureOr<T> computation())

参数类型是FutureOr<T> computation(),表示返回值是FutureOr<T>类型的函数。可以根据该函数创建一个Future对象。而FutureOr<T>是一个union类型,代表参数函数的返回值,可以是T类型,也可以是一个Future<T>类型。

通过这个方法创建的Futurecomputation函数会被添加到event队列中执行。

处理结果

then

创建完成Future对象后,可以通过then方法接收Future的结果。

Future<R> then<R>(FutureOr<R> onValue(T value), {Function onError});

then方法接收两个回调函数参数,onValue接收成功回调,可选的onError接收失败回调。

var future = Future(() {
   //do compution
    return "future value";
  });

future.then((value) {
    print("onValue: $value");
}, onError: (error) {
    print("onError $error");
});

then方法的返回值也是一个Future,可以继续对这个对象调用then方法,注册接收结果的函数。后注册then函数接收的就是前一个then函数的返回值,如果前一个函数没有返回值,后面就接收到null值。 下面代码将会输出 onValue: future value onValue: next value

var future = Future(() {
   //do compution
    return "future value";
  });

future.then((value) {
    print("onValue: $value");
    return "next value";
}, onError: (error) {
    print("onError $error");
}).then((value) {
    print("onValue: $value");
});

then方法的onValue回调函数的返回值为FutureOr<R>,代表该回调函数可以返回一个Future对象,或者返回一个T类型的值。如果是返回的是Future对象,那么后面通过then注册的回调函数将收到该Future的结果。 下面代码将会输出 onValue: future value onValue: request value

var future = Future(() {
   //do compution
    return "future value";
  });

future.then((value) {
    print("onValue: $value");
    var otherFuture = Future(() {
        //do request
        return "request value";
    });
    return otherFuture;
}, onError: (error) {
    print("onError $error");
}).then((value) {
    print("onValue: $value");
});

通过这种方式 ,就可以发起多个异步网络请求,从上到下顺序执行,而不是通过callback进行横向嵌套。

catchError

Future没有正确的获取到结果发生异常时,除了在then方法中注册onError回调外,还可通过catchError方法监听异常。

Future<T> catchError(Function onError, {bool test(Object error)});

下面代码将会输出 catchError Exception: exceptin occured

var future = Future(() {
     //do compution
     throw Exception("exception occured");
  });
  future.then((value){
    print("onValue: $value");
  }).catchError((error) {
    print("catchError $error");
  },);

catchError方法还有一个可以选的test函数参数。当发生异常时,会首先调用test函数,如果该函数返回false,异常将不会被catchError函数处理,会继续传递下去;如果test函数返回ture,catchError函数会处理该异常。如果未提供test函数,默认处理为true。

通过then方法的onError参数和catchError方法处理异常有什么不同呢?

这两种方式主要的区别是,通过catchError方法可以捕获到前一个then方法onValue函数中的异常,而通过onError函数方法,无法捕获同一个then方法onValue函数中的异常。

var future = Future(() {
     return "future value";
  });
  future.then((value){
    print("onValue: $value");
    throw Exception("exception occured");
  }, onError: (error) {
    print("onError $error");
  }).catchError((error) {
    print("catchError $error");
  });

上面代码将会输出 onValue: future value catchError Exception: exception occured 异常被catchError捕获,而onError没有被调用。

whenComplete方法

Future对象还有一个whenComplete方法,当该Future处于完成状态时,通过该方法注册的回调会被调用,无论结果是成功还是失败,相当与finally代码块。 whenComplete方法的参数类型也是FutureOr类型,返回值为Future<T>类型。表示在该方法中可以返回一个值或者是Future对象,在whenComplete函数后,可以再调用thencatchError等方法,但是后面注册回调函数并不能获取到whenComplete中返回的值,而是whenComplete前的值。 该逻辑等效于下面的代码

  Future<T> whenComplete(action()) {
       return this.then((v) {
         var f2 = action();
         if (f2 is Future) return f2.then((_) => v);
         return v
       }, onError: (e) {
         var f2 = action();
         if (f2 is Future) return f2.then((_) { throw e; });
         throw e;
       });
  }

timeout方法

Future<T> timeout(Duration timeLimit, {FutureOr<T> onTimeout()})

timeout方法创建一个新的Future对象,接收一个Duration类型的timeLimit参数来设置超时时间。如果原Future在超时之前完成,最终的结果就是该原Future的值;如果达到超时时间后还未完成,就会产生TimeoutException异常。 该方法有一个onTimeout可选参数,如果设置了该参数,当发生超时时会调用该函数,该函数的返回值为Future的新的值,而不会产生TimeoutException

其他构造方法

sync构造方法

factory Future.sync(FutureOr<T> computation())

将会在当前task执行computation计算,而不是将计算过程添加到任务队列中。

Future.micortask构造方法

factory Future.microtask(FutureOr<T> computation())

通过scheduleMicrotask方法将computation函数添加到microtask队列中,优先于event队列执行。

factory Future.microtask(FutureOr<T> computation()) {
   _Future<T> result = new _Future<T>();
   scheduleMicrotask(() {
     try {
       result._complete(computation());
     } catch (e, s) {
       _completeWithErrorCallback(result, e, s);
     }
   });
   return result;
 }

下面代码将会输出 microtask future default fauture

Future(() {
    print("default fauture");
});

Future.microtask(() {
  print("microtask future");
});

value构造方法

factory Future.value([FutureOr<T> value]) {
    return new _Future<T>.immediate(value);
  }

通过value值创建一个Future对象,vuale可以是一个确定的值或者是一个future对象。该方法使用了内部的immediate函数,这个函数会使用scheduleMicrotaskmicrotask队列中更新Future的值。

error构造方法

factory Future.error(Object error, [StackTrace stackTrace])

通过error对象和可选的stackTrace创建Future,可以使用该方法创建个一个状态为failedFuture对象。

delayed构造方法

factory Future.delayed(Duration duration, [FutureOr<T> computation()]);

创建一个延时执行的Future对象,时间由duration参数设置,到达指定事件后,将会执行computation函数。如果computation函数为空,Future的值为null

wait静态方法

static Future<List<T>> wait<T>(Iterable<Future<T>> futures,{bool eagerError: false, void cleanUp(T successValue)})

类似于JS中Promise的all方法。wait静态方法可以等待多个Future执行完成,并通过List获取所有Future的结果。如果其中一个Future对象发生异常,会导致最终结果为failed。 该方法有两个可选参数,eagerErrorcleanUpeagerError默认值为false,当某一个Future发生异常时,默认不会立刻使Future处于failed状态,而是等待所有Future都有结果后,改变状态为failed。如果设置为true,当其中一个Future发生异常时,会立刻导致最终结果为failed, 如果设置了cleanUp参数,当多个Future中的一个发生异常时,其他成功的Future的(非null)结果会传递给cleanUp参数。如果没有发生异常cleanUp函数不会被调用。

静态方法

any静态方法

static Future<T> any<T>(Iterable<Future<T>> futures)

类似于JS中Promise的race方法。any静态方法可以多个Future中第一个处于完成状态的Future的结果,如果第一个完成的Future处于成功状态,则新的Future状态为成功。如果第一个完成的Future处于失败状态,则新的Future状态为失败。其他Future的状态将被忽略。 wait any方法的参数都可以根据参数类型,推断出新的Future类型。如果参数类型相同,则新的Future类型于参数类型一致,如果不同,则为Object类型。

forEach静态方法

forEach方法有点像Stream的asycMap函数。

static Future forEach<T>(Iterable<T> elements, FutureOr action(T element))

forEach静态方法可以遍历Iterable中的每个元素执行一个操作,如果遍历操作返回的是Future对象,则在该Future完成后再进行下一次遍历,全部完成后返回null。如果在某一次操作中发生异常,会停止遍历,最终的Future的状态为failed。

doWhile静态方法

static Future doWhile(FutureOr<bool> action())

doWhile静态方法是 do while循环的异步版本。可以一直执行一个action,直到action的值为false。 当循环结束时,doWhile方法返回的Future的值也会是null(为什么要说也)。

总结

Future提供了丰富的Api来处理异步任务,除了这些Api外,还有其他工具可以配合使用。比如可以使用FutureBuilder将Future的结果反应到UI界面上;可以使用asStream方法将Future转换为Stream;还可以配合asyncawait来使用,以同步的方式处理异步任务等等。